diff --git a/third-party/nwaku/.dockerignore b/third-party/nwaku/.dockerignore
new file mode 100644
index 0000000..247ac61
--- /dev/null
+++ b/third-party/nwaku/.dockerignore
@@ -0,0 +1,9 @@
+/README.md
+/Dockerfile
+/.*ignore
+/LICENSE*
+/tests
+/metrics
+/nimcache
+librln*
+**/vendor/*
diff --git a/third-party/nwaku/.editorconfig b/third-party/nwaku/.editorconfig
new file mode 100644
index 0000000..e7f569e
--- /dev/null
+++ b/third-party/nwaku/.editorconfig
@@ -0,0 +1,18 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+
+[{Makefile, *.sh}]
+indent_style = tab
+
+# Trailing spaces in markdown indicate word wrap
+[{*.markdown,*.md}]
+trim_trailing_spaces = false
+max_line_length = 80
diff --git a/third-party/nwaku/.github/ISSUE_TEMPLATE/bug_report.md b/third-party/nwaku/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..8e54bbf
--- /dev/null
+++ b/third-party/nwaku/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,34 @@
+---
+name: Bug report
+about: Report any bugs or unexpected behavior
+title: 'bug: '
+labels: bug, track:maintenance
+assignees: ''
+
+---
+
+### Problem
+A clear and concise description of what the bug is.
+
+### Impact
+Indicate how significant you believe the impact of the bug is. Bugs that lead to data loss or corruption would be considered `critical`. In such cases, please also add the `critical` label.
+
+### To reproduce
+If you can reproduce the behavior, steps to reproduce:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+### Expected behavior
+A clear and concise description of what you expected to happen.
+
+### Screenshots/logs
+If applicable, add screenshots or logs to help explain your problem.
+
+### nwaku version/commit hash
+State the version of `nwaku` where you've encountered the bug or, if built off a specific commit, the relevant commit hash. You can check the version by running `./wakunode2 --version`.
+- e.g. `v0.9` or `ed53bcd`
+
+### Additional context
+Add any other context about the problem here.
diff --git a/third-party/nwaku/.github/ISSUE_TEMPLATE/bump_dependencies.md b/third-party/nwaku/.github/ISSUE_TEMPLATE/bump_dependencies.md
new file mode 100644
index 0000000..0413cbf
--- /dev/null
+++ b/third-party/nwaku/.github/ISSUE_TEMPLATE/bump_dependencies.md
@@ -0,0 +1,48 @@
+---
+name: Bump dependencies
+about: Bump vendor dependencies for release
+title: 'Bump vendor dependencies for release 0.0.0'
+labels: dependencies
+assignees: ''
+
+---
+
+
+
+Update `nwaku` "vendor" dependencies.
+
+### Items to bump
+- [ ] dnsclient.nim ( update to the latest tag version )
+- [ ] nim-bearssl
+- [ ] nimbus-build-system
+- [ ] nim-chronicles
+- [ ] nim-chronos
+- [ ] nim-confutils
+- [ ] nimcrypto
+- [ ] nim-dnsdisc
+- [ ] nim-eth
+- [ ] nim-faststreams
+- [ ] nim-http-utils
+- [ ] nim-json-rpc
+- [ ] nim-json-serialization
+- [ ] nim-libbacktrace
+- [ ] nim-libp2p ( update to the latest tag version )
+- [ ] nim-metrics
+- [ ] nim-nat-traversal
+- [ ] nim-presto
+- [ ] nim-regex ( update to the latest tag version )
+- [ ] nim-results
+- [ ] nim-secp256k1
+- [ ] nim-serialization
+- [ ] nim-sqlite3-abi ( update to the latest tag version )
+- [ ] nim-stew
+- [ ] nim-stint
+- [ ] nim-taskpools ( update to the latest tag version )
+- [ ] nim-testutils ( update to the latest tag version )
+- [ ] nim-toml-serialization
+- [ ] nim-unicodedb
+- [ ] nim-unittest2 ( update to the latest tag version )
+- [ ] nim-web3 ( update to the latest tag version )
+- [ ] nim-websock ( update to the latest tag version )
+- [ ] nim-zlib
+- [ ] zerokit ( this should be kept in version `v0.7.0` )
diff --git a/third-party/nwaku/.github/ISSUE_TEMPLATE/feature_request.md b/third-party/nwaku/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..52e2164
--- /dev/null
+++ b/third-party/nwaku/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,26 @@
+---
+name: Feature request
+about: Suggest an idea for the nwaku implementation
+title: 'feat: '
+labels: track:production
+assignees: ''
+
+---
+
+### Problem
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+### Suggested solution
+A clear and concise description of what you want to happen.
+
+### Alternatives considered
+A clear and concise description of any alternative solutions or features you've considered.
+
+### Additional context
+Add any other context or screenshots about the feature request here.
+
+### Acceptance criteria
+A list of tasks that need to be done for the issue to be considered resolved.
+
+### Epic
+Epic title and link the feature refers to.
diff --git a/third-party/nwaku/.github/ISSUE_TEMPLATE/improvement.md b/third-party/nwaku/.github/ISSUE_TEMPLATE/improvement.md
new file mode 100644
index 0000000..5dee34f
--- /dev/null
+++ b/third-party/nwaku/.github/ISSUE_TEMPLATE/improvement.md
@@ -0,0 +1,17 @@
+---
+name: Improvement
+about: Suggest improvements to the codebase or processes. This includes refactoring,
+ docs and any other chores.
+title: 'chore:'
+labels: track:maintenance
+assignees: ''
+
+---
+### Background
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]; There is a spelling error in [...]; It's difficult to read the code in module [...]
+
+### Details
+A clear and concise description of what you want to happen.
+
+### Acceptance criteria
+A list of tasks that need to be done for the issue to be considered resolved.
diff --git a/third-party/nwaku/.github/ISSUE_TEMPLATE/milestone.md b/third-party/nwaku/.github/ISSUE_TEMPLATE/milestone.md
new file mode 100644
index 0000000..d167ce1
--- /dev/null
+++ b/third-party/nwaku/.github/ISSUE_TEMPLATE/milestone.md
@@ -0,0 +1,41 @@
+---
+name: Milestone Issue Template
+about: Track Milestones
+title: "[Milestone] "
+labels: milestone
+assignees: ''
+
+---
+
+
+
+
+**Planned start date**:
+**Due date**:
+
+# Summary
+
+# Acceptance Criteria
+
+
+
+## Tasks
+
+
+
+# RAID (Risks, Assumptions, Issues and Dependencies)
+
+
+
+
+
+
+
+
diff --git a/third-party/nwaku/.github/ISSUE_TEMPLATE/prepare_release.md b/third-party/nwaku/.github/ISSUE_TEMPLATE/prepare_release.md
new file mode 100644
index 0000000..9553d56
--- /dev/null
+++ b/third-party/nwaku/.github/ISSUE_TEMPLATE/prepare_release.md
@@ -0,0 +1,72 @@
+---
+name: Prepare release
+about: Execute tasks for the creation and publishing of a new release
+title: 'Prepare release 0.0.0'
+labels: release
+assignees: ''
+
+---
+
+
+
+### Items to complete
+
+All items below are to be completed by the owner of the given release.
+
+- [ ] Create release branch
+- [ ] Assign release candidate tag to the release branch HEAD. e.g. v0.30.0-rc.0
+- [ ] Generate and edit releases notes in CHANGELOG.md
+- [ ] Review possible update of [config-options](https://github.com/waku-org/docs.waku.org/blob/develop/docs/guides/nwaku/config-options.md)
+- [ ] _End user impact_: Summarize impact of changes on Status end users (can be a comment in this issue).
+- [ ] **Validate release candidate**
+ - [ ] Bump nwaku dependency in [waku-rust-bindings](https://github.com/waku-org/waku-rust-bindings) and make sure all examples and tests work
+
+- [ ] Automated testing
+ - [ ] Ensures js-waku tests are green against release candidate
+ - [ ] Ask Vac-QA and Vac-DST to perform available tests against release candidate
+ - [ ] Vac-QA
+ - [ ] Vac-DST (we need additional report. see [this](https://www.notion.so/DST-Reports-1228f96fb65c80729cd1d98a7496fe6f))
+
+ - [ ] **On Waku fleets**
+ - [ ] Lock `waku.test` fleet to release candidate version
+ - [ ] Continuously stress `waku.test` fleet for a week (e.g. from `wakudev`)
+ - [ ] Search _Kibana_ logs from the previous month (since last release was deployed), for possible crashes or errors in `waku.test` and `waku.sandbox`.
+ - Most relevant logs are `(fleet: "waku.test" OR fleet: "waku.sandbox") AND message: "SIGSEGV"`
+ - [ ] Run release candidate with `waku-simulator`, ensure that nodes connected to each other
+ - [ ] Unlock `waku.test` to resume auto-deployment of latest `master` commit
+
+ - [ ] **On Status fleet**
+ - [ ] Deploy release candidate to `status.staging`
+ - [ ] Perform [sanity check](https://www.notion.so/How-to-test-Nwaku-on-Status-12c6e4b9bf06420ca868bd199129b425) and log results as comments in this issue.
+ - [ ] Connect 2 instances to `status.staging` fleet, one in relay mode, the other one in light client.
+ - [ ] 1:1 Chats with each other
+ - [ ] Send and receive messages in a community
+ - [ ] Close one instance, send messages with second instance, reopen first instance and confirm messages sent while offline are retrieved from store
+ - [ ] Perform checks based _end user impact_
+ - [ ] Inform other (Waku and Status) CCs to point their instance to `status.staging` for a few days. Ping Status colleagues from their Discord server or [Status community](https://status.app/c/G3kAAMSQtb05kog3aGbr3kiaxN4tF5xy4BAGEkkLwILk2z3GcoYlm5hSJXGn7J3laft-tnTwDWmYJ18dP_3bgX96dqr_8E3qKAvxDf3NrrCMUBp4R9EYkQez9XSM4486mXoC3mIln2zc-TNdvjdfL9eHVZ-mGgs=#zQ3shZeEJqTC1xhGUjxuS4rtHSrhJ8vUYp64v6qWkLpvdy9L9) (not blocking point.)
+ - [ ] Ask Status-QA to perform sanity checks (as described above) + checks based on _end user impact_; do specify the version being tested
+ - [ ] Ask Status-QA or infra to run the automated Status e2e tests against `status.staging`
+ - [ ] Get other CCs sign-off: they comment on this PR "used app for a week, no problem", or problem reported, resolved and new RC
+ - [ ] **Get Status-QA sign-off**. Ensuring that `status.test` update will not disturb ongoing activities.
+
+- [ ] **Proceed with release**
+
+ - [ ] Assign a release tag to the same commit that contains the validated release-candidate tag
+ - [ ] Create GitHub release
+ - [ ] Deploy the release to DockerHub
+ - [ ] Announce the release
+
+- [ ] **Promote release to fleets**.
+ - [ ] Update infra config with any deprecated arguments or changed options
+ - [ ] [Deploy final release to `waku.sandbox` fleet](https://ci.infra.status.im/job/nim-waku/job/deploy-waku-sandbox)
+ - [ ] [Deploy final release to `status.staging` fleet](https://ci.infra.status.im/job/nim-waku/job/deploy-shards-staging/)
+ - [ ] [Deploy final release to `status.prod` fleet](https://ci.infra.status.im/job/nim-waku/job/deploy-shards-test/)
+
+- [ ] **Post release**
+ - [ ] Submit a PR from the release branch to master. Important to commit the PR with "create a merge commit" option.
+ - [ ] Update waku-org/nwaku-compose with the new release version.
+ - [ ] Update version in js-waku repo. [update only this](https://github.com/waku-org/js-waku/blob/7c0ce7b2eca31cab837da0251e1e4255151be2f7/.github/workflows/ci.yml#L135) by submitting a PR.
diff --git a/third-party/nwaku/.github/ISSUE_TEMPLATE/research-related-issue.md b/third-party/nwaku/.github/ISSUE_TEMPLATE/research-related-issue.md
new file mode 100644
index 0000000..b90b7d8
--- /dev/null
+++ b/third-party/nwaku/.github/ISSUE_TEMPLATE/research-related-issue.md
@@ -0,0 +1,19 @@
+---
+name: Research-related issue
+about: Use this template if your issue is related to any Vac research tracks
+title: 'research:'
+labels: ''
+assignees: ''
+
+---
+
+### Problem
+
+### Acceptance criteria
+
+### Details
+
+### Possible Solutions
+
+### Research track
+Indicate the Vac research track that this issue relates to. Please also add the relevant track as a label.
diff --git a/third-party/nwaku/.github/pull_request_template.md b/third-party/nwaku/.github/pull_request_template.md
new file mode 100644
index 0000000..b5aba5c
--- /dev/null
+++ b/third-party/nwaku/.github/pull_request_template.md
@@ -0,0 +1,8 @@
+
+## Description
+
+## Changes
+
+## Issue
+
+closes #
diff --git a/third-party/nwaku/.github/workflows/auto_assign_pr.yml b/third-party/nwaku/.github/workflows/auto_assign_pr.yml
new file mode 100644
index 0000000..39847b0
--- /dev/null
+++ b/third-party/nwaku/.github/workflows/auto_assign_pr.yml
@@ -0,0 +1,12 @@
+name: Auto Assign PR to Creator
+
+on:
+ pull_request:
+ types:
+ - opened
+
+jobs:
+ assign_creator:
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: toshimaru/auto-author-assign@v1.6.2
\ No newline at end of file
diff --git a/third-party/nwaku/.github/workflows/ci.yml b/third-party/nwaku/.github/workflows/ci.yml
new file mode 100644
index 0000000..5b32193
--- /dev/null
+++ b/third-party/nwaku/.github/workflows/ci.yml
@@ -0,0 +1,191 @@
+name: ci
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+env:
+ NPROC: 2
+ MAKEFLAGS: "-j${NPROC}"
+ NIMFLAGS: "--parallelBuild:${NPROC} --colors:off -d:chronicles_colors:none"
+
+jobs:
+ changes: # changes detection
+ runs-on: ubuntu-22.04
+ permissions:
+ pull-requests: read
+ steps:
+ - uses: actions/checkout@v4
+ name: Checkout code
+ id: checkout
+ - uses: dorny/paths-filter@v2
+ id: filter
+ with:
+ filters: |
+ common:
+ - '.github/workflows/**'
+ - 'vendor/**'
+ - 'Makefile'
+ - 'waku.nimble'
+ - 'library/**'
+ v2:
+ - 'waku/**'
+ - 'apps/**'
+ - 'tools/**'
+ - 'tests/all_tests_v2.nim'
+ - 'tests/**'
+ docker:
+ - 'docker/**'
+
+ outputs:
+ common: ${{ steps.filter.outputs.common }}
+ v2: ${{ steps.filter.outputs.v2 }}
+ docker: ${{ steps.filter.outputs.docker }}
+
+ build:
+ needs: changes
+ if: ${{ needs.changes.outputs.v2 == 'true' || needs.changes.outputs.common == 'true' }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-22.04, macos-13]
+ runs-on: ${{ matrix.os }}
+ timeout-minutes: 60
+
+ name: build-${{ matrix.os }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Get submodules hash
+ id: submodules
+ run: |
+ echo "hash=$(git submodule status | awk '{print $1}' | sort | shasum -a 256 | sed 's/[ -]*//g')" >> $GITHUB_OUTPUT
+
+ - name: Cache submodules
+ uses: actions/cache@v3
+ with:
+ path: |
+ vendor/
+ .git/modules
+ key: ${{ runner.os }}-vendor-modules-${{ steps.submodules.outputs.hash }}
+
+ - name: Build binaries
+ run: make V=1 QUICK_AND_DIRTY_COMPILER=1 all tools
+
+ build-windows:
+ needs: changes
+ if: ${{ needs.changes.outputs.v2 == 'true' || needs.changes.outputs.common == 'true' }}
+ uses: ./.github/workflows/windows-build.yml
+ with:
+ branch: ${{ github.ref }}
+
+ test:
+ needs: changes
+ if: ${{ needs.changes.outputs.v2 == 'true' || needs.changes.outputs.common == 'true' }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-22.04, macos-13]
+ runs-on: ${{ matrix.os }}
+ timeout-minutes: 60
+
+ name: test-${{ matrix.os }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Get submodules hash
+ id: submodules
+ run: |
+ echo "hash=$(git submodule status | awk '{print $1}' | sort | shasum -a 256 | sed 's/[ -]*//g')" >> $GITHUB_OUTPUT
+
+ - name: Cache submodules
+ uses: actions/cache@v3
+ with:
+ path: |
+ vendor/
+ .git/modules
+ key: ${{ runner.os }}-vendor-modules-${{ steps.submodules.outputs.hash }}
+
+ - name: Run tests
+ run: |
+ postgres_enabled=0
+ if [ ${{ runner.os }} == "Linux" ]; then
+ sudo docker run --rm -d -e POSTGRES_PASSWORD=test123 -p 5432:5432 postgres:15.4-alpine3.18
+ postgres_enabled=1
+ fi
+
+ export MAKEFLAGS="-j1"
+ export NIMFLAGS="--colors:off -d:chronicles_colors:none"
+ export USE_LIBBACKTRACE=0
+
+ make V=1 LOG_LEVEL=DEBUG QUICK_AND_DIRTY_COMPILER=1 POSTGRES=$postgres_enabled test
+ make V=1 LOG_LEVEL=DEBUG QUICK_AND_DIRTY_COMPILER=1 POSTGRES=$postgres_enabled testwakunode2
+
+ build-docker-image:
+ needs: changes
+ if: ${{ needs.changes.outputs.v2 == 'true' || needs.changes.outputs.common == 'true' || needs.changes.outputs.docker == 'true' }}
+ uses: waku-org/nwaku/.github/workflows/container-image.yml@master
+ secrets: inherit
+
+ nwaku-nwaku-interop-tests:
+ needs: build-docker-image
+ uses: waku-org/waku-interop-tests/.github/workflows/nim_waku_PR.yml@SMOKE_TEST_0.0.1
+ with:
+ node_nwaku: ${{ needs.build-docker-image.outputs.image }}
+
+ secrets: inherit
+
+ js-waku-node:
+ needs: build-docker-image
+ uses: waku-org/js-waku/.github/workflows/test-node.yml@master
+ with:
+ nim_wakunode_image: ${{ needs.build-docker-image.outputs.image }}
+ test_type: node
+
+ js-waku-node-optional:
+ needs: build-docker-image
+ uses: waku-org/js-waku/.github/workflows/test-node.yml@master
+ with:
+ nim_wakunode_image: ${{ needs.build-docker-image.outputs.image }}
+ test_type: node-optional
+
+ lint:
+ name: "Lint"
+ runs-on: ubuntu-22.04
+ needs: build
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Get submodules hash
+ id: submodules
+ run: |
+ echo "hash=$(git submodule status | awk '{print $1}' | sort | shasum -a 256 | sed 's/[ -]*//g')" >> $GITHUB_OUTPUT
+
+ - name: Cache submodules
+ uses: actions/cache@v3
+ with:
+ path: |
+ vendor/
+ .git/modules
+ key: ${{ runner.os }}-vendor-modules-${{ steps.submodules.outputs.hash }}
+
+ - name: Build nph
+ run: |
+ make build-nph
+
+ - name: Check nph formatting
+ run: |
+ shopt -s extglob # Enable extended globbing
+ NPH=$(make print-nph-path)
+ echo "using nph at ${NPH}"
+ "${NPH}" examples waku tests tools apps *.@(nim|nims|nimble)
+ git diff --exit-code
diff --git a/third-party/nwaku/.github/workflows/container-image.yml b/third-party/nwaku/.github/workflows/container-image.yml
new file mode 100644
index 0000000..cfa66d2
--- /dev/null
+++ b/third-party/nwaku/.github/workflows/container-image.yml
@@ -0,0 +1,99 @@
+name: container-image-build
+
+on:
+ workflow_call:
+ inputs:
+ image_tag:
+ type: string
+ default: ${{ github.event.number }}
+ outputs:
+ image:
+ description: The resulting image link
+ value: ${{ jobs.build-docker-image.outputs.image }}
+
+env:
+ NPROC: 2
+ MAKEFLAGS: "-j${NPROC}"
+ NIMFLAGS: "--parallelBuild:${NPROC}"
+
+# This workflow should not run for outside contributors
+# If org secrets are not available, we'll avoid building and publishing the docker image and we'll pass the workflow
+jobs:
+ build-docker-image:
+ strategy:
+ matrix:
+ os: [ubuntu-22.04]
+ runs-on: ${{ matrix.os }}
+ timeout-minutes: 60
+
+ name: docker-build-${{ matrix.os }}
+ outputs:
+ image: ${{ steps.build.outputs.image }}
+ steps:
+ - name: Check secrets
+ id: secrets
+ continue-on-error: true
+ run: |
+ if [[ -z "$QUAY_PASSWORD" || -z "$QUAY_USER" ]]; then
+ echo "User does not have access to secrets, skipping workflow"
+ exit 1
+ fi
+ env:
+ QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
+ QUAY_USER: ${{ secrets.QUAY_USER }}
+
+ - name: Checkout code
+ if: ${{ steps.secrets.outcome == 'success' }}
+ uses: actions/checkout@v4
+
+ - name: Get submodules hash
+ id: submodules
+ if: ${{ steps.secrets.outcome == 'success' }}
+ run: |
+ echo "hash=$(git submodule status | awk '{print $1}' | sort | shasum -a 256 | sed 's/[ -]*//g')" >> $GITHUB_OUTPUT
+
+ - name: Cache submodules
+ if: ${{ steps.secrets.outcome == 'success' }}
+ uses: actions/cache@v3
+ with:
+ path: |
+ vendor/
+ .git/modules
+ key: ${{ runner.os }}-vendor-modules-${{ steps.submodules.outputs.hash }}
+
+ - name: Build binaries
+ id: build
+ if: ${{ steps.secrets.outcome == 'success' }}
+ run: |
+
+ make -j${NPROC} V=1 QUICK_AND_DIRTY_COMPILER=1 NIMFLAGS="-d:disableMarchNative -d:postgres -d:chronicles_colors:none" wakunode2
+
+ SHORT_REF=$(git rev-parse --short HEAD)
+
+ TAG=$([ "${PR_NUMBER}" == "" ] && echo "${SHORT_REF}" || echo "${PR_NUMBER}")
+ IMAGE=quay.io/wakuorg/nwaku-pr:${TAG}
+
+ echo "image=${IMAGE}" >> $GITHUB_OUTPUT
+ echo "commit_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
+
+ docker login -u ${QUAY_USER} -p ${QUAY_PASSWORD} quay.io
+ docker build -t ${IMAGE} -f docker/binaries/Dockerfile.bn.amd64 --label quay.expires-after=30d .
+ docker push ${IMAGE}
+ env:
+ QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
+ QUAY_USER: ${{ secrets.QUAY_USER }}
+ PR_NUMBER: ${{ inputs.image_tag}}
+
+ - name: Comment PR
+ uses: thollander/actions-comment-pull-request@v2
+ if: ${{ github.event_name == 'pull_request' && steps.secrets.outcome == 'success' }}
+ with:
+ message: |
+ You can find the image built from this PR at
+
+ ```
+ ${{steps.build.outputs.image}}
+ ```
+
+ Built from ${{ steps.build.outputs.commit_hash }}
+ comment_tag: execution-rln-v${{ matrix.rln_version }}
diff --git a/third-party/nwaku/.github/workflows/pr-lint.yml b/third-party/nwaku/.github/workflows/pr-lint.yml
new file mode 100644
index 0000000..d3ac05f
--- /dev/null
+++ b/third-party/nwaku/.github/workflows/pr-lint.yml
@@ -0,0 +1,54 @@
+name: "Lint PR"
+
+on:
+ pull_request_target:
+ types:
+ - opened
+ - edited
+ - synchronize
+
+jobs:
+ labels:
+ runs-on: ubuntu-22.04
+
+ steps:
+ - uses: actions/checkout@v4
+ name: Checkout code
+ id: checkout
+ - uses: dorny/paths-filter@v2
+ id: filter
+ with:
+ filters: |
+ config:
+ - 'apps/wakunode2/external_config.nim'
+ - 'apps/networkmonitor/networkmonitor_config.nim'
+ - 'apps/chat2/config_chat2.nim'
+ - 'apps/chat2bridge/config_chat2bridge.nim'
+
+ db_schema:
+ - 'waku/waku_archive/driver/postgres_driver/postgres_driver.nim'
+ - 'waku/waku_archive/driver/sqlite_driver/queries.nim'
+ - name: Comment config change
+ uses: thollander/actions-comment-pull-request@v2
+ if: ${{steps.filter.outputs.config == 'true'}}
+ with:
+ message: |
+ This PR may contain changes to **configuration options** of one of the apps.
+
+ If you are introducing a breaking change (i.e. the set of options in latest release would no longer be applicable) make sure the original option is preserved with a *deprecation* note for 2 following releases before it is actually removed.
+
+ Please also make sure the label `release-notes` is added to make sure any changes to the user interface are properly announced in changelog and release notes.
+ comment_tag: configs
+
+ - name: Comment DB schema change
+ uses: thollander/actions-comment-pull-request@v2
+ if: ${{steps.filter.outputs.db_schema == 'true'}}
+ with:
+ header: pr-title-lint-error
+ message: |
+ This PR may contain changes to **database schema** of one of the drivers.
+
+ If you are introducing any changes to the schema, make sure the upgrade from the latest release to this change passes without any errors/issues.
+
+ Please make sure the label `release-notes` is added to make sure upgrade instructions properly highlight this change.
+ comment_tag: db_schema
diff --git a/third-party/nwaku/.github/workflows/pre-release.yml b/third-party/nwaku/.github/workflows/pre-release.yml
new file mode 100644
index 0000000..b138a22
--- /dev/null
+++ b/third-party/nwaku/.github/workflows/pre-release.yml
@@ -0,0 +1,163 @@
+name: Pre-Release
+
+on:
+ push:
+ tags:
+ - 'v*-rc.*'
+ schedule:
+ - cron: 13 3 * * *
+ workflow_dispatch:
+
+env:
+ RELEASE_NAME: nightly
+
+ NPROC: 2
+ MAKEFLAGS: "-j${NPROC}"
+ NIMFLAGS: "--parallelBuild:${NPROC}"
+
+jobs:
+ tag-name:
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Vars
+ id: vars
+ run: |
+ TAG=$([[ "${{github.ref}}" == "refs/heads/master" ]] && echo "${{env.RELEASE_NAME}}" || echo ${{github.ref}} | sed 's#refs/tags/##')
+ echo "tag=${TAG}" >> $GITHUB_OUTPUT
+ outputs:
+ tag: ${{steps.vars.outputs.tag}}
+
+ build-and-publish:
+ needs: tag-name
+ strategy:
+ matrix:
+ os: [ubuntu-22.04, macos-13]
+ arch: [amd64]
+ include:
+ - os: macos-13
+ arch: arm64
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: prep variables
+ id: vars
+ run: |
+ ARCH=${{matrix.arch}}
+
+ echo "arch=${ARCH}" >> $GITHUB_OUTPUT
+
+ NWAKU_ARTIFACT_NAME=$(echo "nwaku-${ARCH}-${{runner.os}}-${{ needs.tag-name.outputs.tag }}.tar.gz" | tr "[:upper:]" "[:lower:]")
+ NWAKU_TOOLS_ARTIFACT_NAME=$(echo "nwaku-tools-${ARCH}-${{runner.os}}-${{ needs.tag-name.outputs.tag }}.tar.gz" | tr "[:upper:]" "[:lower:]")
+
+ echo "nwaku=${NWAKU_ARTIFACT_NAME}" >> $GITHUB_OUTPUT
+ echo "nwakutools=${NWAKU_TOOLS_ARTIFACT_NAME}" >> $GITHUB_OUTPUT
+
+
+ - name: build artifacts
+ id: build
+ run: |
+ OS=$([[ "${{runner.os}}" == "macOS" ]] && echo "macosx" || echo "linux")
+
+ make QUICK_AND_DIRTY_COMPILER=1 V=1 CI=false NIMFLAGS="-d:disableMarchNative --os:${OS} --cpu:${{matrix.arch}}" \
+ update
+
+ make QUICK_AND_DIRTY_COMPILER=1 V=1 CI=false\
+ NIMFLAGS="-d:disableMarchNative --os:${OS} --cpu:${{matrix.arch}} -d:postgres" \
+ wakunode2\
+ chat2\
+ tools
+
+ tar -cvzf ${{steps.vars.outputs.nwaku}} ./build/wakunode2 ./build/chat2
+ tar -cvzf ${{steps.vars.outputs.nwakutools}} ./build/wakucanary ./build/networkmonitor
+
+ - name: upload artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: wakunode2
+ path: ${{steps.vars.outputs.nwaku}}
+ retention-days: 2
+
+ - name: upload artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: wakutools
+ path: ${{steps.vars.outputs.nwakutools}}
+ retention-days: 2
+
+ build-docker-image:
+ needs: tag-name
+ uses: waku-org/nwaku/.github/workflows/container-image.yml@master
+ with:
+ image_tag: ${{ needs.tag-name.outputs.tag }}
+ secrets: inherit
+
+ js-waku-node:
+ needs: build-docker-image
+ uses: waku-org/js-waku/.github/workflows/test-node.yml@master
+ with:
+ nim_wakunode_image: ${{ needs.build-docker-image.outputs.image }}
+ test_type: node
+ debug: waku*
+
+ js-waku-node-optional:
+ needs: build-docker-image
+ uses: waku-org/js-waku/.github/workflows/test-node.yml@master
+ with:
+ nim_wakunode_image: ${{ needs.build-docker-image.outputs.image }}
+ test_type: node-optional
+ debug: waku*
+
+ create-release-candidate:
+ runs-on: ubuntu-22.04
+ needs: [ tag-name, build-and-publish ]
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ ref: master
+
+ - name: download artifacts
+ uses: actions/download-artifact@v4
+
+ - name: prep variables
+ id: vars
+ run: |
+ REF=$(echo ${{github.ref}} | sed 's#.*/##')
+
+ echo "ref=${REF}" >> $GITHUB_OUTPUT
+
+ - name: generate release notes
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ set -x
+ gh release view ${{ needs.tag-name.outputs.tag }} &>/dev/null &&\
+ gh release delete -y ${{ needs.tag-name.outputs.tag }} &&\
+ [[ "${{ needs.tag-name.outputs.tag }}" == "nightly" ]] && git tag -d ${{ needs.tag-name.outputs.tag }}
+
+ RELEASE_NOTES_TAG=$([[ "${{ needs.tag-name.outputs.tag }}" != "nightly" ]] && echo "-t ${{steps.vars.outputs.ref}}" || echo "")
+
+ docker run \
+ -t \
+ --rm \
+ -v ${PWD}:/opt/sv4git/repo:z \
+ -u $(id -u) \
+ docker.io/wakuorg/sv4git:latest \
+ release-notes ${RELEASE_NOTES_TAG} --previous $(git tag -l --sort -creatordate | grep -e "^v[0-9]*\.[0-9]*\.[0-9]*$") |\
+ sed -E 's@#([0-9]+)@[#\1](https://github.com/waku-org/nwaku/issues/\1)@g' > release_notes.md
+
+ sed -i "s/^## .*/Generated at $(date)/" release_notes.md
+
+ cat release_notes.md
+
+ TARGET=$([[ "${{ needs.tag-name.outputs.tag }}" == "nightly" ]] && echo "--target ${{steps.vars.outputs.ref}}" || echo "")
+
+ gh release create ${{ needs.tag-name.outputs.tag }} --prerelease ${TARGET} \
+ --title ${{ needs.tag-name.outputs.tag }} --notes-file release_notes.md \
+ wakunode2/* wakutools/*
diff --git a/third-party/nwaku/.github/workflows/release-assets.yml b/third-party/nwaku/.github/workflows/release-assets.yml
new file mode 100644
index 0000000..2c7c260
--- /dev/null
+++ b/third-party/nwaku/.github/workflows/release-assets.yml
@@ -0,0 +1,65 @@
+name: Upload Release Asset
+
+on:
+ push:
+ tags:
+ - 'v*' # "e.g. v0.4"
+
+ workflow_dispatch:
+
+env:
+ NPROC: 2
+
+jobs:
+ build-and-upload:
+ strategy:
+ matrix:
+ os: [ubuntu-22.04, macos-13]
+ arch: [amd64]
+ include:
+ - os: macos-13
+ arch: arm64
+ runs-on: ${{ matrix.os }}
+ timeout-minutes: 60
+
+ name: ${{ matrix.os }} - ${{ matrix.arch }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: Get submodules hash
+ id: submodules
+ run: |
+ echo "hash=$(git submodule status | awk '{print $1}' | sort | shasum -a 256 | sed 's/[ -]*//g')" >> $GITHUB_OUTPUT
+
+ - name: Cache submodules
+ uses: actions/cache@v3
+ with:
+ path: |
+ vendor/
+ .git/modules
+ key: ${{ runner.os }}-${{matrix.arch}}-submodules-${{ steps.submodules.outputs.hash }}
+
+ - name: prep variables
+ id: vars
+ run: |
+ NWAKU_ARTIFACT_NAME=$(echo "nwaku-${{matrix.arch}}-${{runner.os}}.tar.gz" | tr "[:upper:]" "[:lower:]")
+
+ echo "nwaku=${NWAKU_ARTIFACT_NAME}" >> $GITHUB_OUTPUT
+
+ - name: Install dependencies
+ run: |
+ OS=$([[ "${{runner.os}}" == "macOS" ]] && echo "macosx" || echo "linux")
+
+ make -j${NPROC} NIMFLAGS="--parallelBuild:${NPROC} -d:disableMarchNative --os:${OS} --cpu:${{matrix.arch}}" V=1 update
+ make -j${NPROC} NIMFLAGS="--parallelBuild:${NPROC} -d:disableMarchNative --os:${OS} --cpu:${{matrix.arch}} -d:postgres" CI=false wakunode2
+ make -j${NPROC} NIMFLAGS="--parallelBuild:${NPROC} -d:disableMarchNative --os:${OS} --cpu:${{matrix.arch}}" CI=false chat2
+ tar -cvzf ${{steps.vars.outputs.nwaku}} ./build/
+
+ - name: Upload asset
+ uses: actions/upload-artifact@v4.4.0
+ with:
+ name: ${{steps.vars.outputs.nwaku}}
+ path: ${{steps.vars.outputs.nwaku}}
+ if-no-files-found: error
diff --git a/third-party/nwaku/.github/workflows/sync-labels.yml b/third-party/nwaku/.github/workflows/sync-labels.yml
new file mode 100644
index 0000000..e53797b
--- /dev/null
+++ b/third-party/nwaku/.github/workflows/sync-labels.yml
@@ -0,0 +1,17 @@
+name: Sync labels
+on:
+ push:
+ branches:
+ - master
+ paths:
+ - .github/labels.yml
+jobs:
+ build:
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v2
+ - uses: micnncim/action-label-syncer@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ manifest: .github/labels.yml
diff --git a/third-party/nwaku/.github/workflows/windows-build.yml b/third-party/nwaku/.github/workflows/windows-build.yml
new file mode 100644
index 0000000..ed6d2cb
--- /dev/null
+++ b/third-party/nwaku/.github/workflows/windows-build.yml
@@ -0,0 +1,104 @@
+name: ci / build-windows
+
+on:
+ workflow_call:
+ inputs:
+ branch:
+ required: true
+ type: string
+
+jobs:
+ build:
+ runs-on: windows-latest
+
+ defaults:
+ run:
+ shell: msys2 {0}
+
+ env:
+ MSYSTEM: MINGW64
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Setup MSYS2
+ uses: msys2/setup-msys2@v2
+ with:
+ update: true
+ install: >-
+ git
+ base-devel
+ mingw-w64-x86_64-toolchain
+ make
+ cmake
+ upx
+ mingw-w64-x86_64-rust
+ mingw-w64-x86_64-postgresql
+ mingw-w64-x86_64-gcc
+ mingw-w64-x86_64-gcc-libs
+ mingw-w64-x86_64-libwinpthread-git
+ mingw-w64-x86_64-zlib
+ mingw-w64-x86_64-openssl
+ mingw-w64-x86_64-python
+ mingw-w64-x86_64-cmake
+ mingw-w64-x86_64-llvm
+ mingw-w64-x86_64-clang
+
+ - name: Add UPX to PATH
+ run: |
+ echo "/usr/bin:$PATH" >> $GITHUB_PATH
+ echo "/mingw64/bin:$PATH" >> $GITHUB_PATH
+ echo "/usr/lib:$PATH" >> $GITHUB_PATH
+ echo "/mingw64/lib:$PATH" >> $GITHUB_PATH
+
+ - name: Verify dependencies
+ run: |
+ which upx gcc g++ make cmake cargo rustc python
+
+ - name: Updating submodules
+ run: git submodule update --init --recursive
+
+ - name: Creating tmp directory
+ run: mkdir -p tmp
+
+ - name: Building Nim
+ run: |
+ cd vendor/nimbus-build-system/vendor/Nim
+ ./build_all.bat
+ cd ../../../..
+
+ - name: Building miniupnpc
+ run: |
+ cd vendor/nim-nat-traversal/vendor/miniupnp/miniupnpc
+ make -f Makefile.mingw CC=gcc CXX=g++ libminiupnpc.a V=1
+ cd ../../../../..
+
+ - name: Building libnatpmp
+ run: |
+ cd ./vendor/nim-nat-traversal/vendor/libnatpmp-upstream
+ make CC="gcc -fPIC -D_WIN32_WINNT=0x0600 -DNATPMP_STATICLIB" libnatpmp.a V=1
+ cd ../../../../
+
+ - name: Building wakunode2.exe
+ run: |
+ make wakunode2 LOG_LEVEL=DEBUG V=3 -j8
+
+ - name: Building libwaku.dll
+ run: |
+ make libwaku STATIC=0 LOG_LEVEL=DEBUG V=1 -j
+
+ - name: Check Executable
+ run: |
+ if [ -f "./build/wakunode2.exe" ]; then
+ echo "wakunode2.exe build successful"
+ else
+ echo "Build failed: wakunode2.exe not found"
+ exit 1
+ fi
+ if [ -f "./build/libwaku.dll" ]; then
+ echo "libwaku.dll build successful"
+ else
+ echo "Build failed: libwaku.dll not found"
+ exit 1
+ fi
diff --git a/third-party/nwaku/.gitignore b/third-party/nwaku/.gitignore
new file mode 100644
index 0000000..7430c3e
--- /dev/null
+++ b/third-party/nwaku/.gitignore
@@ -0,0 +1,81 @@
+/nimcache
+
+# Executables shall be put in an ignored build/ directory
+/build
+
+# Nimble packages
+/vendor/.nimble
+
+# Generated Files
+*.generated.nim
+
+# ntags/ctags output
+/tags
+
+# a symlink that can't be added to the repo because of Windows
+/waku.nims
+
+# Ignore dynamic, static libs and libtool archive files
+*.so
+*.dylib
+*.a
+*.la
+*.exe
+*.dll
+
+.DS_Store
+
+# Ignore simulation generated metrics files
+/metrics/prometheus
+/metrics/waku-sim-all-nodes-grafana-dashboard.json
+
+*.log
+/package-lock.json
+/package.json
+node_modules/
+/.update.timestamp
+
+# Ignore Jetbrains IDE files
+.idea/
+
+# ignore vscode files
+.vscode/
+
+# RLN / keystore
+rlnKeystore.json
+*.tar.gz
+
+# Nimbus Build System
+nimbus-build-system.paths
+
+# sqlite db
+*.db
+*.db-shm
+*.db-wal
+*.sqlite3
+*.sqlite3-shm
+*.sqlite3-wal
+
+/examples/nodejs/build/
+/examples/rust/target/
+
+
+# Coverage
+coverage_html_report/
+*.info
+
+# Wildcard
+*.ignore.*
+
+# Ignore all possible node runner directories
+**/keystore/
+**/rln_tree/
+**/certs/
+
+# simple qt example
+.qmake.stash
+main-qt
+waku_handler.moc.cpp
+
+# Nix build result
+result
diff --git a/third-party/nwaku/.gitmodules b/third-party/nwaku/.gitmodules
new file mode 100644
index 0000000..408def1
--- /dev/null
+++ b/third-party/nwaku/.gitmodules
@@ -0,0 +1,190 @@
+[submodule "vendor/nim-eth"]
+ path = vendor/nim-eth
+ url = https://github.com/status-im/nim-eth.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-secp256k1"]
+ path = vendor/nim-secp256k1
+ url = https://github.com/status-im/nim-secp256k1.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-libp2p"]
+ path = vendor/nim-libp2p
+ url = https://github.com/vacp2p/nim-libp2p.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-stew"]
+ path = vendor/nim-stew
+ url = https://github.com/status-im/nim-stew.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nimbus-build-system"]
+ path = vendor/nimbus-build-system
+ url = https://github.com/status-im/nimbus-build-system.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-nat-traversal"]
+ path = vendor/nim-nat-traversal
+ url = https://github.com/status-im/nim-nat-traversal.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-libbacktrace"]
+ path = vendor/nim-libbacktrace
+ url = https://github.com/status-im/nim-libbacktrace.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-confutils"]
+ path = vendor/nim-confutils
+ url = https://github.com/status-im/nim-confutils.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-chronicles"]
+ path = vendor/nim-chronicles
+ url = https://github.com/status-im/nim-chronicles.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-faststreams"]
+ path = vendor/nim-faststreams
+ url = https://github.com/status-im/nim-faststreams.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-chronos"]
+ path = vendor/nim-chronos
+ url = https://github.com/status-im/nim-chronos.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-json-serialization"]
+ path = vendor/nim-json-serialization
+ url = https://github.com/status-im/nim-json-serialization.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-serialization"]
+ path = vendor/nim-serialization
+ url = https://github.com/status-im/nim-serialization.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nimcrypto"]
+ path = vendor/nimcrypto
+ url = https://github.com/cheatfate/nimcrypto.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-metrics"]
+ path = vendor/nim-metrics
+ url = https://github.com/status-im/nim-metrics.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-stint"]
+ path = vendor/nim-stint
+ url = https://github.com/status-im/nim-stint.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-json-rpc"]
+ path = vendor/nim-json-rpc
+ url = https://github.com/status-im/nim-json-rpc.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-http-utils"]
+ path = vendor/nim-http-utils
+ url = https://github.com/status-im/nim-http-utils.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-bearssl"]
+ path = vendor/nim-bearssl
+ url = https://github.com/status-im/nim-bearssl.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-sqlite3-abi"]
+ path = vendor/nim-sqlite3-abi
+ url = https://github.com/arnetheduck/nim-sqlite3-abi.git
+ ignore = dirty
+ branch = master
+[submodule "vendor/nim-web3"]
+ path = vendor/nim-web3
+ url = https://github.com/status-im/nim-web3.git
+[submodule "vendor/nim-testutils"]
+ path = vendor/nim-testutils
+ url = https://github.com/status-im/nim-testutils.git
+ ignore = untracked
+ branch = master
+[submodule "vendor/nim-unittest2"]
+ path = vendor/nim-unittest2
+ url = https://github.com/status-im/nim-unittest2.git
+ ignore = untracked
+ branch = master
+[submodule "vendor/nim-websock"]
+ path = vendor/nim-websock
+ url = https://github.com/status-im/nim-websock.git
+ ignore = untracked
+ branch = main
+[submodule "vendor/nim-zlib"]
+ path = vendor/nim-zlib
+ url = https://github.com/status-im/nim-zlib.git
+ ignore = untracked
+ branch = master
+[submodule "vendor/nim-dnsdisc"]
+ path = vendor/nim-dnsdisc
+ url = https://github.com/status-im/nim-dnsdisc.git
+ ignore = untracked
+ branch = main
+[submodule "vendor/dnsclient.nim"]
+ path = vendor/dnsclient.nim
+ url = https://github.com/ba0f3/dnsclient.nim.git
+ ignore = untracked
+ branch = master
+[submodule "vendor/nim-toml-serialization"]
+ path = vendor/nim-toml-serialization
+ url = https://github.com/status-im/nim-toml-serialization.git
+[submodule "vendor/nim-presto"]
+ path = vendor/nim-presto
+ url = https://github.com/status-im/nim-presto.git
+ ignore = untracked
+ branch = master
+[submodule "vendor/zerokit"]
+ path = vendor/zerokit
+ url = https://github.com/vacp2p/zerokit.git
+ ignore = dirty
+ branch = v0.5.1
+[submodule "vendor/nim-regex"]
+ path = vendor/nim-regex
+ url = https://github.com/nitely/nim-regex.git
+ ignore = untracked
+ branch = master
+[submodule "vendor/nim-unicodedb"]
+ path = vendor/nim-unicodedb
+ url = https://github.com/nitely/nim-unicodedb.git
+ ignore = untracked
+ branch = master
+[submodule "vendor/nim-taskpools"]
+ path = vendor/nim-taskpools
+ url = https://github.com/status-im/nim-taskpools.git
+ ignore = untracked
+ branch = stable
+[submodule "vendor/nim-results"]
+ ignore = untracked
+ branch = master
+ path = vendor/nim-results
+ url = https://github.com/arnetheduck/nim-results.git
+[submodule "vendor/db_connector"]
+ path = vendor/db_connector
+ url = https://github.com/nim-lang/db_connector.git
+ ignore = untracked
+ branch = devel
+[submodule "vendor/nph"]
+ ignore = untracked
+ branch = master
+ path = vendor/nph
+ url = https://github.com/arnetheduck/nph.git
+[submodule "vendor/nim-minilru"]
+ path = vendor/nim-minilru
+ url = https://github.com/status-im/nim-minilru.git
+ ignore = untracked
+ branch = master
+[submodule "vendor/waku-rlnv2-contract"]
+ path = vendor/waku-rlnv2-contract
+ url = https://github.com/waku-org/waku-rlnv2-contract.git
+ ignore = untracked
+ branch = master
+[submodule "vendor/mix"]
+ path = vendor/mix
+ url = https://github.com/vacp2p/mix/
+ branch = main
diff --git a/third-party/nwaku/.sv4git.yml b/third-party/nwaku/.sv4git.yml
new file mode 100644
index 0000000..8975e69
--- /dev/null
+++ b/third-party/nwaku/.sv4git.yml
@@ -0,0 +1,22 @@
+version: "1.1" #config version
+
+tag:
+ pattern: "v%d.%d.%d"
+ filter: "v*"
+
+release-notes:
+ sections: # Array with each section of release note. Check template section for more information.
+ - name: Features # Name used on section.
+ section-type: commits # Type of the section, supported types: commits, breaking-changes.
+ commit-types: [feat] # Commit types for commit section-type, one commit type cannot be in more than one section.
+ - name: Bug Fixes
+ section-type: commits
+ commit-types: [fix, bug]
+ - name: Changes
+ section-type: commits
+ commit-types: [chore, docs, build, refactor, docker]
+
+commit-message:
+
+ issue:
+ regex: '#[0-9]+' # Regex for issue id.
\ No newline at end of file
diff --git a/third-party/nwaku/.sv4git/templates/releasenotes-md.tpl b/third-party/nwaku/.sv4git/templates/releasenotes-md.tpl
new file mode 100644
index 0000000..a513e69
--- /dev/null
+++ b/third-party/nwaku/.sv4git/templates/releasenotes-md.tpl
@@ -0,0 +1,8 @@
+## {{if .Release}}{{.Release}}{{end}}{{if and (not .Date.IsZero) .Release}} ({{end}}{{timefmt .Date "2006-01-02"}}{{if and (not .Date.IsZero) .Release}}){{end}}
+{{- range $section := .Sections }}
+{{- if (eq $section.SectionType "commits") }}
+{{- template "rn-md-section-commits.tpl" $section }}
+{{- else if (eq $section.SectionType "breaking-changes")}}
+{{- template "rn-md-section-breaking-changes.tpl" $section }}
+{{- end}}
+{{- end}}
diff --git a/third-party/nwaku/.sv4git/templates/rn-md-section-commits.tpl b/third-party/nwaku/.sv4git/templates/rn-md-section-commits.tpl
new file mode 100644
index 0000000..2732fcd
--- /dev/null
+++ b/third-party/nwaku/.sv4git/templates/rn-md-section-commits.tpl
@@ -0,0 +1,7 @@
+{{- if .}}{{- if ne .SectionName ""}}
+
+### {{.SectionName}}
+{{range $k,$v := .Items}}
+- {{if $v.Message.Scope}}**{{$v.Message.Scope}}:** {{end}}{{$v.Message.Description}} ([{{$v.Hash}}](https://github.com/waku-org/nwaku/commit/{{$v.Hash}})){{if $v.Message.Metadata.issue}} ([https://github.com/waku-org/nwaku/issues/{{$v.Message.Metadata.issue}}]({{$v.Message.Metadata.issue}})){{end}}
+{{- end}}
+{{- end}}{{- end}}
\ No newline at end of file
diff --git a/third-party/nwaku/CHANGELOG.md b/third-party/nwaku/CHANGELOG.md
new file mode 100644
index 0000000..dc07379
--- /dev/null
+++ b/third-party/nwaku/CHANGELOG.md
@@ -0,0 +1,2495 @@
+## v0.36.0 (2025-06-20)
+### Notes
+
+- Extended REST API for better debugging
+ - Extended `/health` report
+ - Very detailed access to peers and actual status through [`/admin/v1/peers/...` endpoints](https://waku-org.github.io/waku-rest-api/#get-/admin/v1/peers/stats)
+ - Dynamic log level change with[ `/admin/v1/log-level`](https://waku-org.github.io/waku-rest-api/#post-/admin/v1/log-level/-logLevel-)
+
+- The `rln-relay-eth-client-address` parameter, from now on, should be passed as an array of RPC addresses.
+- new `preset` parameter. `preset=twn` is the RLN-protected Waku Network (cluster 1). Overrides other values.
+- Removed `dns-addrs` parameter as it was duplicated and unused.
+- Removed `rln-relay-id-key`, `rln-relay-id-commitment-key`, `rln-relay-bandwidth-threshold` parameters.
+- Effectively removed `pubsub-topic`, which was deprecated in `v0.33.0`.
+- Removed `store-sync-max-payload-size` parameter.
+- Removed `dns-discovery-name-server` and `discv5-only` parameters.
+
+### Features
+
+- Update implementation for new contract abi ([#3390](https://github.com/waku-org/nwaku/issues/3390)) ([ee4058b2d](https://github.com/waku-org/nwaku/commit/ee4058b2d))
+- Lighptush v3 for lite-protocol-tester ([#3455](https://github.com/waku-org/nwaku/issues/3455)) ([3f3c59488](https://github.com/waku-org/nwaku/commit/3f3c59488))
+- Retrieve metrics from libwaku ([#3452](https://github.com/waku-org/nwaku/issues/3452)) ([f016ede60](https://github.com/waku-org/nwaku/commit/f016ede60))
+- Dynamic logging via REST API ([#3451](https://github.com/waku-org/nwaku/issues/3451)) ([9fe8ef8d2](https://github.com/waku-org/nwaku/commit/9fe8ef8d2))
+- Add waku_disconnect_all_peers to libwaku ([#3438](https://github.com/waku-org/nwaku/issues/3438)) ([7f51d103b](https://github.com/waku-org/nwaku/commit/7f51d103b))
+- Extend node /health REST endpoint with all protocol's state ([#3419](https://github.com/waku-org/nwaku/issues/3419)) ([1632496a2](https://github.com/waku-org/nwaku/commit/1632496a2))
+- Deprecate sync / local merkle tree ([#3312](https://github.com/waku-org/nwaku/issues/3312)) ([50fe7d727](https://github.com/waku-org/nwaku/commit/50fe7d727))
+- Refactor waku sync DOS protection ([#3391](https://github.com/waku-org/nwaku/issues/3391)) ([a81f9498c](https://github.com/waku-org/nwaku/commit/a81f9498c))
+- Waku Sync dashboard new panel & update ([#3379](https://github.com/waku-org/nwaku/issues/3379)) ([5ed6aae10](https://github.com/waku-org/nwaku/commit/5ed6aae10))
+- Enhance Waku Sync logs and metrics ([#3370](https://github.com/waku-org/nwaku/issues/3370)) ([f6c680a46](https://github.com/waku-org/nwaku/commit/f6c680a46))
+- Add waku_get_connected_peers_info to libwaku ([#3356](https://github.com/waku-org/nwaku/issues/3356)) ([0eb9c6200](https://github.com/waku-org/nwaku/commit/0eb9c6200))
+- Add waku_relay_get_peers_in_mesh to libwaku ([#3352](https://github.com/waku-org/nwaku/issues/3352)) ([ef9074443](https://github.com/waku-org/nwaku/commit/ef9074443))
+- Add waku_relay_get_connected_peers to libwaku ([#3353](https://github.com/waku-org/nwaku/issues/3353)) ([7250d7392](https://github.com/waku-org/nwaku/commit/7250d7392))
+- Introduce `preset` option ([#3346](https://github.com/waku-org/nwaku/issues/3346)) ([0eaf90465](https://github.com/waku-org/nwaku/commit/0eaf90465))
+- Add store sync dashboard panel ([#3307](https://github.com/waku-org/nwaku/issues/3307)) ([ef8ee233f](https://github.com/waku-org/nwaku/commit/ef8ee233f))
+
+### Bug Fixes
+
+- Fix typo from DIRVER to DRIVER ([#3442](https://github.com/waku-org/nwaku/issues/3442)) ([b9a4d7702](https://github.com/waku-org/nwaku/commit/b9a4d7702))
+- Fix discv5 protocol id in libwaku ([#3447](https://github.com/waku-org/nwaku/issues/3447)) ([f7be4c2f0](https://github.com/waku-org/nwaku/commit/f7be4c2f0))
+- Fix dnsresolver ([#3440](https://github.com/waku-org/nwaku/issues/3440)) ([e42e28cc6](https://github.com/waku-org/nwaku/commit/e42e28cc6))
+- Misc sync fixes, added debug logging ([#3411](https://github.com/waku-org/nwaku/issues/3411)) ([b9efa874d](https://github.com/waku-org/nwaku/commit/b9efa874d))
+- Relay unsubscribe ([#3422](https://github.com/waku-org/nwaku/issues/3422)) ([9fc631e10](https://github.com/waku-org/nwaku/commit/9fc631e10))
+- Fix build_rln.sh update version to download v0.7.0 ([#3425](https://github.com/waku-org/nwaku/issues/3425)) ([2678303bf](https://github.com/waku-org/nwaku/commit/2678303bf))
+- Timestamp based validation ([#3406](https://github.com/waku-org/nwaku/issues/3406)) ([1512bdaf0](https://github.com/waku-org/nwaku/commit/1512bdaf0))
+- Enable WebSocket connection also in case only websocket-secure-support enabled ([#3417](https://github.com/waku-org/nwaku/issues/3417)) ([698fe6525](https://github.com/waku-org/nwaku/commit/698fe6525))
+- Fix addPeer could unintentionally override metadata of previously stored peer with defaults and empty ([#3403](https://github.com/waku-org/nwaku/issues/3403)) ([5cccaaac6](https://github.com/waku-org/nwaku/commit/5cccaaac6))
+- Fix bad HttpCode conversion, add missing lightpush v3 rest api tests ([#3389](https://github.com/waku-org/nwaku/issues/3389)) ([7ff055e42](https://github.com/waku-org/nwaku/commit/7ff055e42))
+- Adjust mistaken comments and broken link ([#3381](https://github.com/waku-org/nwaku/issues/3381)) ([237f7abbb](https://github.com/waku-org/nwaku/commit/237f7abbb))
+- Avoid libwaku's redundant allocs ([#3380](https://github.com/waku-org/nwaku/issues/3380)) ([ac454a30b](https://github.com/waku-org/nwaku/commit/ac454a30b))
+- Avoid performing nil check for userData ([#3365](https://github.com/waku-org/nwaku/issues/3365)) ([b8707b6a5](https://github.com/waku-org/nwaku/commit/b8707b6a5))
+- Fix waku sync timing ([#3337](https://github.com/waku-org/nwaku/issues/3337)) ([b01b1837d](https://github.com/waku-org/nwaku/commit/b01b1837d))
+- Fix filter out ephemeral msg from waku sync ([#3332](https://github.com/waku-org/nwaku/issues/3332)) ([4b963d8f5](https://github.com/waku-org/nwaku/commit/4b963d8f5))
+- Apply latest nph formating ([#3334](https://github.com/waku-org/nwaku/issues/3334)) ([77105a6c2](https://github.com/waku-org/nwaku/commit/77105a6c2))
+- waku sync 2.0 codecs ENR support ([#3326](https://github.com/waku-org/nwaku/issues/3326)) ([bf735e777](https://github.com/waku-org/nwaku/commit/bf735e777))
+- waku sync mounting ([#3321](https://github.com/waku-org/nwaku/issues/3321)) ([380d2e338](https://github.com/waku-org/nwaku/commit/380d2e338))
+- Fix rest-relay-cache-capacity ([#3454](https://github.com/waku-org/nwaku/issues/3454)) ([fed4dc280](https://github.com/waku-org/nwaku/commit/fed4dc280))
+
+### Changes
+
+- Lower waku sync log lvl ([#3461](https://github.com/waku-org/nwaku/issues/3461)) ([4277a5349](https://github.com/waku-org/nwaku/commit/4277a5349))
+- Refactor to unify online and health monitors ([#3456](https://github.com/waku-org/nwaku/issues/3456)) ([2e40f2971](https://github.com/waku-org/nwaku/commit/2e40f2971))
+- Refactor rm discv5-only ([#3453](https://github.com/waku-org/nwaku/issues/3453)) ([b998430d5](https://github.com/waku-org/nwaku/commit/b998430d5))
+- Add extra debug REST helper via getting peer statistics ([#3443](https://github.com/waku-org/nwaku/issues/3443)) ([f4ad7a332](https://github.com/waku-org/nwaku/commit/f4ad7a332))
+- Expose online state in libwaku ([#3433](https://github.com/waku-org/nwaku/issues/3433)) ([e7f5c8cb2](https://github.com/waku-org/nwaku/commit/e7f5c8cb2))
+- Add heaptrack support build for Nim v2.0.12 builds ([#3424](https://github.com/waku-org/nwaku/issues/3424)) ([91885fb9e](https://github.com/waku-org/nwaku/commit/91885fb9e))
+- Remove debug for js-waku ([#3423](https://github.com/waku-org/nwaku/issues/3423)) ([5628dc6ad](https://github.com/waku-org/nwaku/commit/5628dc6ad))
+- Bump dependencies for v0.36 ([#3410](https://github.com/waku-org/nwaku/issues/3410)) ([005815746](https://github.com/waku-org/nwaku/commit/005815746))
+- Enhance feedback on error CLI ([#3405](https://github.com/waku-org/nwaku/issues/3405)) ([3464d81a6](https://github.com/waku-org/nwaku/commit/3464d81a6))
+- Allow multiple rln eth clients ([#3402](https://github.com/waku-org/nwaku/issues/3402)) ([861710bc7](https://github.com/waku-org/nwaku/commit/861710bc7))
+- Separate internal and CLI configurations ([#3357](https://github.com/waku-org/nwaku/issues/3357)) ([dd8d66431](https://github.com/waku-org/nwaku/commit/dd8d66431))
+- Avoid double relay subscription ([#3396](https://github.com/waku-org/nwaku/issues/3396)) ([7d5eb9374](https://github.com/waku-org/nwaku/commit/7d5eb9374) [#3429](https://github.com/waku-org/nwaku/issues/3429)) ([ee5932ebc](https://github.com/waku-org/nwaku/commit/ee5932ebc))
+- Improve disconnection handling ([#3385](https://github.com/waku-org/nwaku/issues/3385)) ([1ec9b8d96](https://github.com/waku-org/nwaku/commit/1ec9b8d96))
+- Return all peers from REST admin ([#3395](https://github.com/waku-org/nwaku/issues/3395)) ([f6fdd960f](https://github.com/waku-org/nwaku/commit/f6fdd960f))
+- Simplify rln_relay code a little ([#3392](https://github.com/waku-org/nwaku/issues/3392)) ([7a6c00bd0](https://github.com/waku-org/nwaku/commit/7a6c00bd0))
+- Extended /admin/v1 RESP API with different option to look at current connected/relay/mesh state of the node ([#3382](https://github.com/waku-org/nwaku/issues/3382)) ([3db00f39e](https://github.com/waku-org/nwaku/commit/3db00f39e))
+- Timestamp set to now in publish if not provided ([#3373](https://github.com/waku-org/nwaku/issues/3373)) ([f7b424451](https://github.com/waku-org/nwaku/commit/f7b424451))
+- Update lite-protocol-tester for handling shard argument ([#3371](https://github.com/waku-org/nwaku/issues/3371)) ([5ab69edd7](https://github.com/waku-org/nwaku/commit/5ab69edd7))
+- Fix unused and deprecated imports ([#3368](https://github.com/waku-org/nwaku/issues/3368)) ([6ebb49a14](https://github.com/waku-org/nwaku/commit/6ebb49a14))
+- Expect camelCase JSON for libwaku store queries ([#3366](https://github.com/waku-org/nwaku/issues/3366)) ([ccb4ed51d](https://github.com/waku-org/nwaku/commit/ccb4ed51d))
+- Maintenance to c and c++ simple examples ([#3367](https://github.com/waku-org/nwaku/issues/3367)) ([25d30d44d](https://github.com/waku-org/nwaku/commit/25d30d44d))
+- Skip two flaky tests ([#3364](https://github.com/waku-org/nwaku/issues/3364)) ([b672617b2](https://github.com/waku-org/nwaku/commit/b672617b2))
+- Retrieve protocols in new added peer from discv5 ([#3354](https://github.com/waku-org/nwaku/issues/3354)) ([df58643ea](https://github.com/waku-org/nwaku/commit/df58643ea))
+- Better keystore management ([#3358](https://github.com/waku-org/nwaku/issues/3358)) ([a914fdccc](https://github.com/waku-org/nwaku/commit/a914fdccc))
+- Remove pubsub topics arguments ([#3350](https://github.com/waku-org/nwaku/issues/3350)) ([9778b45c6](https://github.com/waku-org/nwaku/commit/9778b45c6))
+- New performance measurement metrics for non-relay protocols ([#3299](https://github.com/waku-org/nwaku/issues/3299)) ([68c50a09a](https://github.com/waku-org/nwaku/commit/68c50a09a))
+- Start triggering CI for windows build ([#3316](https://github.com/waku-org/nwaku/issues/3316)) ([55ac6ba9f](https://github.com/waku-org/nwaku/commit/55ac6ba9f))
+- Less logs for rendezvous ([#3319](https://github.com/waku-org/nwaku/issues/3319)) ([6df05bae2](https://github.com/waku-org/nwaku/commit/6df05bae2))
+- Add test reporting doc to benchmarks dir ([#3238](https://github.com/waku-org/nwaku/issues/3238)) ([94554a6e0](https://github.com/waku-org/nwaku/commit/94554a6e0))
+- Improve epoch monitoring ([#3197](https://github.com/waku-org/nwaku/issues/3197)) ([b0c025f81](https://github.com/waku-org/nwaku/commit/b0c025f81))
+
+### This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`WAKU2-LIGHTPUSH v3`](https://github.com/waku-org/specs/blob/master/standards/core/lightpush.md) | `draft` | `/vac/waku/lightpush/3.0.0` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+| [`WAKU-SYNC`](https://github.com/waku-org/specs/blob/feat--waku-sync/standards/core/sync.md) | `draft` | `/vac/waku/sync/1.0.0` |
+
+
+## v0.35.1 (2025-03-30)
+
+### Bug fixes
+
+* Update RLN references ([3287](https://github.com/waku-org/nwaku/pull/3287)) ([ea961fa](https://github.com/waku-org/nwaku/pull/3287/commits/ea961faf4ed4f8287a2043a6b5d84b660745072b))
+
+**Info:** before upgrading to this version, make sure you delete the previous rln_tree folder, i.e.,
+the one that is passed through this CLI: `--rln-relay-tree-path`.
+
+### Features
+* lightpush v3 ([#3279](https://github.com/waku-org/nwaku/pull/3279)) ([e0b563ff](https://github.com/waku-org/nwaku/commit/e0b563ffe5af20bd26d37cd9b4eb9ed9eb82ff80))
+ Upgrade for Waku Llightpush protocol with enhanced error handling. Read specification [here](https://github.com/waku-org/specs/blob/master/standards/core/lightpush.md)
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`WAKU2-LIGHTPUSH v3`](https://github.com/waku-org/specs/blob/master/standards/core/lightpush.md) | `draft` | `/vac/waku/lightpush/3.0.0` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+| [`WAKU-SYNC`](https://github.com/waku-org/specs/blob/feat--waku-sync/standards/core/sync.md) | `draft` | `/vac/waku/sync/1.0.0` |
+
+## v0.35.0 (2025-03-03)
+
+### Notes
+
+- Deprecated parameter
+ - max-relay-peers
+
+- New parameters
+ - relay-service-ratio
+
+ String value with peers distribution within max-connections parameter.
+ This percentage ratio represents the relay peers to service peers.
+ For example, 60:40, tells that 60% of the max-connections will be used for relay protocol
+ and the other 40% of max-connections will be reserved for other service protocols (e.g.,
+ filter, lightpush, store, metadata, etc.)
+
+ - rendezvous
+
+ boolean attribute that optionally activates waku rendezvous discovery server.
+ True by default.
+
+### Release highlights
+
+- New filter approach to keep push stream opened within subscription period.
+- Waku sync protocol.
+- Libwaku async
+- Lite-protocol-tester enhancements.
+- New panels and metrics in RLN to control outstanding request quota.
+
+### Features
+
+- waku sync shard matching check ([#3259](https://github.com/waku-org/nwaku/issues/3259)) ([42fd6b827](https://github.com/waku-org/nwaku/commit/42fd6b827))
+- waku store sync 2.0 config & setup ([#3217](https://github.com/waku-org/nwaku/issues/3217)) ([7f64dc03a](https://github.com/waku-org/nwaku/commit/7f64dc03a))
+- waku store sync 2.0 protocols & tests ([#3216](https://github.com/waku-org/nwaku/issues/3216)) ([6ee494d90](https://github.com/waku-org/nwaku/commit/6ee494d90))
+- waku store sync 2.0 storage & tests ([#3215](https://github.com/waku-org/nwaku/issues/3215)) ([54a7a6875](https://github.com/waku-org/nwaku/commit/54a7a6875))
+- waku store sync 2.0 common types & codec ([#3213](https://github.com/waku-org/nwaku/issues/3213)) ([29fda2dab](https://github.com/waku-org/nwaku/commit/29fda2dab))
+- add txhash-based eligibility checks for incentivization PoC ([#3166](https://github.com/waku-org/nwaku/issues/3166)) ([505ec84ce](https://github.com/waku-org/nwaku/commit/505ec84ce))
+- connection change event ([#3225](https://github.com/waku-org/nwaku/issues/3225)) ([e81a5517b](https://github.com/waku-org/nwaku/commit/e81a5517b))
+- libwaku add protected topic ([#3211](https://github.com/waku-org/nwaku/issues/3211)) ([d932dd10c](https://github.com/waku-org/nwaku/commit/d932dd10c))
+- topic health tracking ([#3212](https://github.com/waku-org/nwaku/issues/3212)) ([6020a673b](https://github.com/waku-org/nwaku/commit/6020a673b))
+- allowing configuration of application level callbacks ([#3206](https://github.com/waku-org/nwaku/issues/3206)) ([049fbeabb](https://github.com/waku-org/nwaku/commit/049fbeabb))
+- waku rendezvous wrapper ([#2962](https://github.com/waku-org/nwaku/issues/2962)) ([650a9487e](https://github.com/waku-org/nwaku/commit/650a9487e))
+- making dns discovery async ([#3175](https://github.com/waku-org/nwaku/issues/3175)) ([d7d00bfd7](https://github.com/waku-org/nwaku/commit/d7d00bfd7))
+- remove Waku Sync 1.0 & Negentropy ([#3185](https://github.com/waku-org/nwaku/issues/3185)) ([2ab9c3d36](https://github.com/waku-org/nwaku/commit/2ab9c3d36))
+- add waku_dial_peer and get_connected_peers to libwaku ([#3149](https://github.com/waku-org/nwaku/issues/3149)) ([507b1fc4d](https://github.com/waku-org/nwaku/commit/507b1fc4d))
+- running periodicaly peer exchange if discv5 is disabled ([#3150](https://github.com/waku-org/nwaku/issues/3150)) ([400d7a54f](https://github.com/waku-org/nwaku/commit/400d7a54f))
+
+### Bug Fixes
+
+- avoid double db migration for sqlite ([#3244](https://github.com/waku-org/nwaku/issues/3244)) ([2ce245354](https://github.com/waku-org/nwaku/commit/2ce245354))
+- libwaku waku_relay_unsubscribe ([#3207](https://github.com/waku-org/nwaku/issues/3207)) ([ab0c1d4aa](https://github.com/waku-org/nwaku/commit/ab0c1d4aa))
+- libwaku support string and int64 for timestamps ([#3205](https://github.com/waku-org/nwaku/issues/3205)) ([2022f54f5](https://github.com/waku-org/nwaku/commit/2022f54f5))
+- lite-protocol-tester receiver exit check ([#3187](https://github.com/waku-org/nwaku/issues/3187)) ([beb21c78f](https://github.com/waku-org/nwaku/commit/beb21c78f))
+- linting error ([#3156](https://github.com/waku-org/nwaku/issues/3156)) ([99ac68447](https://github.com/waku-org/nwaku/commit/99ac68447))
+
+### Changes
+
+- more efficient metrics usage ([#3298](https://github.com/waku-org/nwaku/issues/3298)) ([6f004d5d4](https://github.com/waku-org/nwaku/commit/6f004d5d4))([c07e278d8](https://github.com/waku-org/nwaku/commit/c07e278d82c3aa771b9988e85bad7422890e4d74))
+- filter refactor subscription management and react when the remote peer closes the stream. See the following commits in chronological order:
+ - issue: [#3281](https://github.com/waku-org/nwaku/issues/3281) commit: [5392b8ea4](https://github.com/waku-org/nwaku/commit/5392b8ea4)
+ - issue: [#3198](https://github.com/waku-org/nwaku/issues/3198) commit: [287e9b12c](https://github.com/waku-org/nwaku/commit/287e9b12c)
+ - issue: [#3267](https://github.com/waku-org/nwaku/issues/3267) commit: [46747fd49](https://github.com/waku-org/nwaku/commit/46747fd49)
+- send msg hash as string on libwaku message event ([#3234](https://github.com/waku-org/nwaku/issues/3234)) ([9c209b4c3](https://github.com/waku-org/nwaku/commit/9c209b4c3))
+- separate heaptrack from debug build ([#3249](https://github.com/waku-org/nwaku/issues/3249)) ([81f24cc25](https://github.com/waku-org/nwaku/commit/81f24cc25))
+- capping mechanism for relay and service connections ([#3184](https://github.com/waku-org/nwaku/issues/3184)) ([2942782f9](https://github.com/waku-org/nwaku/commit/2942782f9))
+- add extra migration to sqlite and improving error message ([#3240](https://github.com/waku-org/nwaku/issues/3240)) ([bfd60ceab](https://github.com/waku-org/nwaku/commit/bfd60ceab))
+- optimize libwaku size ([#3242](https://github.com/waku-org/nwaku/issues/3242)) ([9c0ad8517](https://github.com/waku-org/nwaku/commit/9c0ad8517))
+- golang example end using negentropy dependency plus simple readme.md ([#3235](https://github.com/waku-org/nwaku/issues/3235)) ([0e0fcfb1a](https://github.com/waku-org/nwaku/commit/0e0fcfb1a))
+- enhance libwaku store protocol and more ([#3223](https://github.com/waku-org/nwaku/issues/3223)) ([22ce9ee87](https://github.com/waku-org/nwaku/commit/22ce9ee87))
+- add two RLN metrics and panel ([#3181](https://github.com/waku-org/nwaku/issues/3181)) ([1b532e8ab](https://github.com/waku-org/nwaku/commit/1b532e8ab))
+- libwaku async ([#3180](https://github.com/waku-org/nwaku/issues/3180)) ([47a623541](https://github.com/waku-org/nwaku/commit/47a623541))
+- filter protocol in libwaku ([#3177](https://github.com/waku-org/nwaku/issues/3177)) ([f856298ca](https://github.com/waku-org/nwaku/commit/f856298ca))
+- add supervisor for lite-protocol-tester infra ([#3176](https://github.com/waku-org/nwaku/issues/3176)) ([a7264d68c](https://github.com/waku-org/nwaku/commit/a7264d68c))
+- libwaku better error handling and better waku thread destroy handling ([#3167](https://github.com/waku-org/nwaku/issues/3167)) ([294dd03c4](https://github.com/waku-org/nwaku/commit/294dd03c4))
+- libwaku allow several multiaddresses for a single peer in store queries ([#3171](https://github.com/waku-org/nwaku/issues/3171)) ([3cb8ebdd8](https://github.com/waku-org/nwaku/commit/3cb8ebdd8))
+- naming connectPeer procedure ([#3157](https://github.com/waku-org/nwaku/issues/3157)) ([b3656d6ee](https://github.com/waku-org/nwaku/commit/b3656d6ee))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+| [`WAKU-SYNC`](https://github.com/waku-org/specs/blob/master/standards/core/sync.md) | `draft` | `/vac/waku/reconciliation/1.0.0` & `/vac/waku/transfer/1.0.0` |
+
+## v0.34.0 (2024-10-29)
+
+### Notes:
+
+* The `--protected-topic` CLI configuration has been removed. Equivalent flag, `--protected-shard`, shall be used instead.
+
+### Features
+
+- change latency buckets ([#3153](https://github.com/waku-org/nwaku/issues/3153)) ([956fde6e](https://github.com/waku-org/nwaku/commit/956fde6e))
+- libwaku: ping peer ([#3144](https://github.com/waku-org/nwaku/issues/3144)) ([de11e576](https://github.com/waku-org/nwaku/commit/de11e576))
+- initial windows support ([#3107](https://github.com/waku-org/nwaku/issues/3107)) ([ff21c01e](https://github.com/waku-org/nwaku/commit/ff21c01e))
+- circuit relay support ([#3112](https://github.com/waku-org/nwaku/issues/3112)) ([cfde7eea](https://github.com/waku-org/nwaku/commit/cfde7eea))
+
+### Bug Fixes
+
+- peer exchange libwaku response handling ([#3141](https://github.com/waku-org/nwaku/issues/3141)) ([76606421](https://github.com/waku-org/nwaku/commit/76606421))
+- add more logs, stagger intervals & set prune offset to 10% for waku sync ([#3142](https://github.com/waku-org/nwaku/issues/3142)) ([a386880b](https://github.com/waku-org/nwaku/commit/a386880b))
+- add log and archive message ingress for sync ([#3133](https://github.com/waku-org/nwaku/issues/3133)) ([80c7581a](https://github.com/waku-org/nwaku/commit/80c7581a))
+- add a limit of max 10 content topics per query ([#3117](https://github.com/waku-org/nwaku/issues/3117)) ([c35dc549](https://github.com/waku-org/nwaku/commit/c35dc549))
+- avoid segfault by setting a default num peers requested in Peer eXchange ([#3122](https://github.com/waku-org/nwaku/issues/3122)) ([82fd5dde](https://github.com/waku-org/nwaku/commit/82fd5dde))
+- returning peerIds in base 64 ([#3105](https://github.com/waku-org/nwaku/issues/3105)) ([37edaf62](https://github.com/waku-org/nwaku/commit/37edaf62))
+- changing libwaku's error handling format ([#3093](https://github.com/waku-org/nwaku/issues/3093)) ([2e6c299d](https://github.com/waku-org/nwaku/commit/2e6c299d))
+- remove spammy log ([#3091](https://github.com/waku-org/nwaku/issues/3091)) ([1d2b910f](https://github.com/waku-org/nwaku/commit/1d2b910f))
+- avoid out connections leak ([#3077](https://github.com/waku-org/nwaku/issues/3077)) ([eb2bbae6](https://github.com/waku-org/nwaku/commit/eb2bbae6))
+- rejecting excess relay connections ([#3065](https://github.com/waku-org/nwaku/issues/3065)) ([8b0884c7](https://github.com/waku-org/nwaku/commit/8b0884c7))
+- static linking negentropy in ARM based mac ([#3046](https://github.com/waku-org/nwaku/issues/3046)) ([256b7853](https://github.com/waku-org/nwaku/commit/256b7853))
+
+### Changes
+
+- support ping with multiple multiaddresses and close stream ([#3154](https://github.com/waku-org/nwaku/issues/3154)) ([3665991a](https://github.com/waku-org/nwaku/commit/3665991a))
+- liteprotocoltester: easy setup fleets ([#3125](https://github.com/waku-org/nwaku/issues/3125)) ([268e7e66](https://github.com/waku-org/nwaku/commit/268e7e66))
+- saving peers enr capabilities ([#3127](https://github.com/waku-org/nwaku/issues/3127)) ([69d9524f](https://github.com/waku-org/nwaku/commit/69d9524f))
+- networkmonitor: add missing field on RlnRelay init, set default for num of shard ([#3136](https://github.com/waku-org/nwaku/issues/3136)) ([edcb0e15](https://github.com/waku-org/nwaku/commit/edcb0e15))
+- add to libwaku peer id retrieval proc ([#3124](https://github.com/waku-org/nwaku/issues/3124)) ([c5a825e2](https://github.com/waku-org/nwaku/commit/c5a825e2))
+- adding to libwaku dial and disconnect by peerIds ([#3111](https://github.com/waku-org/nwaku/issues/3111)) ([25da8102](https://github.com/waku-org/nwaku/commit/25da8102))
+- dbconn: add requestId info as a comment in the database logs ([#3110](https://github.com/waku-org/nwaku/issues/3110)) ([30c072a4](https://github.com/waku-org/nwaku/commit/30c072a4))
+- improving get_peer_ids_by_protocol by returning the available protocols of connected peers ([#3109](https://github.com/waku-org/nwaku/issues/3109)) ([ed0ee5be](https://github.com/waku-org/nwaku/commit/ed0ee5be))
+- remove warnings ([#3106](https://github.com/waku-org/nwaku/issues/3106)) ([c861fa9f](https://github.com/waku-org/nwaku/commit/c861fa9f))
+- better store logs ([#3103](https://github.com/waku-org/nwaku/issues/3103)) ([21b03551](https://github.com/waku-org/nwaku/commit/21b03551))
+- Improve binding for waku_sync ([#3102](https://github.com/waku-org/nwaku/issues/3102)) ([c3756e3a](https://github.com/waku-org/nwaku/commit/c3756e3a))
+- improving and temporarily skipping flaky rln test ([#3094](https://github.com/waku-org/nwaku/issues/3094)) ([a6ed80a5](https://github.com/waku-org/nwaku/commit/a6ed80a5))
+- update master after release v0.33.1 ([#3089](https://github.com/waku-org/nwaku/issues/3089)) ([54c3083d](https://github.com/waku-org/nwaku/commit/54c3083d))
+- re-arrange function based on responsibility of peer-manager ([#3086](https://github.com/waku-org/nwaku/issues/3086)) ([0f8e8740](https://github.com/waku-org/nwaku/commit/0f8e8740))
+- waku_keystore: give some more context in case of error ([#3064](https://github.com/waku-org/nwaku/issues/3064)) ([3ad613ca](https://github.com/waku-org/nwaku/commit/3ad613ca))
+- bump negentropy ([#3078](https://github.com/waku-org/nwaku/issues/3078)) ([643ab20f](https://github.com/waku-org/nwaku/commit/643ab20f))
+- Optimize store ([#3061](https://github.com/waku-org/nwaku/issues/3061)) ([5875ed63](https://github.com/waku-org/nwaku/commit/5875ed63))
+- wrap peer store ([#3051](https://github.com/waku-org/nwaku/issues/3051)) ([729e63f5](https://github.com/waku-org/nwaku/commit/729e63f5))
+- disabling metrics for libwaku ([#3058](https://github.com/waku-org/nwaku/issues/3058)) ([b358c90f](https://github.com/waku-org/nwaku/commit/b358c90f))
+- test peer connection management ([#3049](https://github.com/waku-org/nwaku/issues/3049)) ([711e7db1](https://github.com/waku-org/nwaku/commit/711e7db1))
+- updating upload and download artifact actions to v4 ([#3047](https://github.com/waku-org/nwaku/issues/3047)) ([7c4a9717](https://github.com/waku-org/nwaku/commit/7c4a9717))
+- Better database query logs and logarithmic scale in grafana store panels ([#3048](https://github.com/waku-org/nwaku/issues/3048)) ([d68b06f1](https://github.com/waku-org/nwaku/commit/d68b06f1))
+- extending store metrics ([#3042](https://github.com/waku-org/nwaku/issues/3042)) ([fd83b42f](https://github.com/waku-org/nwaku/commit/fd83b42f))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+| [`WAKU-SYNC`](https://github.com/waku-org/specs/blob/master/standards/core/sync.md) | `draft` | `/vac/waku/sync/1.0.0` |
+
+## v0.33.1 (2024-10-03)
+
+### Bug fixes
+
+* Fix out connections leak ([3077](https://github.com/waku-org/nwaku/pull/3077)) ([eb2bbae6](https://github.com/waku-org/nwaku/commit/eb2bbae6))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+| [`WAKU-SYNC`](https://github.com/waku-org/specs/blob/feat--waku-sync/standards/core/sync.md) | `draft` | `/vac/waku/sync/1.0.0` |
+
+## v0.33.0 (2024-09-30)
+
+#### Notes:
+
+* The `--pubsub-topic` CLI configuration has been deprecated and support for it will be removed on release v0.35.0. In order to migrate, please use the `--shard` configuration instead. For example, instead of `--pubsub-topic=/waku/2/rs//`, use `--cluster-id=` once and `--shard=` for each subscribed shard
+* The `--rest-private` CLI configuration has been removed. Please delete any reference to it when running your nodes
+* Introduced the `--reliability` CLI configuration, activating the new experimental StoreV3 message confirmation protocol
+* DOS protection configurations of non-relay, req/resp protocols are changed
+ * `--request-rate-limit` and `--request-rate-period` options are no longer supported.
+ * `--rate-limit` CLI configuration is now available.
+ - The new flag can describe various rate-limit requirements for each protocol supported. The setting can be repeated, each instance can define exactly one rate-limit option.
+ - Format is `:volume/period`
+ - If protocol is not given, settings will be taken as default for un-set protocols. Ex: 80/2s
+ - Supported protocols are: lightpush|filter|px|store|storev2|storev3
+ - `volume` must be an integer value, representing number of requests over the period of time allowed.
+ - `period ` must be an integer with defined unit as one of h|m|s|ms
+ - If not set, no rate limit will be applied to request/response protocols, except for the filter protocol.
+
+
+### Release highlights
+
+* a new experimental reliability protocol has been implemented, leveraging StoreV3 to confirm message delivery
+* Peer Exchange protocol can now be protected by rate-limit boundary checks.
+* Fine-grained configuration of DOS protection is available with this release. See, "Notes" above.
+
+### Bug Fixes
+
+- rejecting excess relay connections ([#3063](https://github.com/waku-org/nwaku/issues/3063)) ([8b0884c7](https://github.com/waku-org/nwaku/commit/8b0884c7))
+- make Peer Exchange's rpc status_code optional for backward compatibility ([#3059](https://github.com/waku-org/nwaku/pull/3059)) ([5afa9b13](https://github.com/waku-org/nwaku/commit/5afa9b13))
+- px protocol decode - do not treat missing response field as error ([#3054](https://github.com/waku-org/nwaku/issues/3054)) ([9b445ac4](https://github.com/waku-org/nwaku/commit/9b445ac4))
+- setting up node with modified config ([#3036](https://github.com/waku-org/nwaku/issues/3036)) ([8f289925](https://github.com/waku-org/nwaku/commit/8f289925))
+- get back health check for postgres legacy ([#3010](https://github.com/waku-org/nwaku/issues/3010)) ([5a0edff7](https://github.com/waku-org/nwaku/commit/5a0edff7))
+- libnegentropy integration ([#2996](https://github.com/waku-org/nwaku/issues/2996)) ([c3cb06ac](https://github.com/waku-org/nwaku/commit/c3cb06ac))
+- peer-exchange issue ([#2889](https://github.com/waku-org/nwaku/issues/2889)) ([43157102](https://github.com/waku-org/nwaku/commit/43157102))
+
+### Changes
+
+- append current version in agentString which is used by the identify protocol ([#3057](https://github.com/waku-org/nwaku/pull/3057)) ([368bb3c1](https://github.com/waku-org/nwaku/commit/368bb3c1))
+- rate limit peer exchange protocol, enhanced response status in RPC ([#3035](https://github.com/waku-org/nwaku/issues/3035)) ([0a7f16a3](https://github.com/waku-org/nwaku/commit/0a7f16a3))
+- Switch libnegentropy library build from shared to static linkage ([#3041](https://github.com/waku-org/nwaku/issues/3041)) ([83f25c3e](https://github.com/waku-org/nwaku/commit/83f25c3e))
+- libwaku reduce repetitive code by adding a template handling resp returns ([#3032](https://github.com/waku-org/nwaku/issues/3032)) ([1713f562](https://github.com/waku-org/nwaku/commit/1713f562))
+- libwaku - extending the library with peer_manager and peer_exchange features ([#3026](https://github.com/waku-org/nwaku/issues/3026)) ([5ea1cf0c](https://github.com/waku-org/nwaku/commit/5ea1cf0c))
+- use submodule nph in CI to check lint ([#3027](https://github.com/waku-org/nwaku/issues/3027)) ([ce9a8c46](https://github.com/waku-org/nwaku/commit/ce9a8c46))
+- deprecating pubsub topic ([#2997](https://github.com/waku-org/nwaku/issues/2997)) ([a3cd2a1a](https://github.com/waku-org/nwaku/commit/a3cd2a1a))
+- lightpush - error metric less variable by only setting a fixed string ([#3020](https://github.com/waku-org/nwaku/issues/3020)) ([d3e6717a](https://github.com/waku-org/nwaku/commit/d3e6717a))
+- enhance libpq management ([#3015](https://github.com/waku-org/nwaku/issues/3015)) ([45319f09](https://github.com/waku-org/nwaku/commit/45319f09))
+- per limit split of PostgreSQL queries ([#3008](https://github.com/waku-org/nwaku/issues/3008)) ([e1e05afb](https://github.com/waku-org/nwaku/commit/e1e05afb))
+- Added metrics to liteprotocoltester ([#3002](https://github.com/waku-org/nwaku/issues/3002)) ([8baf627f](https://github.com/waku-org/nwaku/commit/8baf627f))
+- extending store metrics ([#2995](https://github.com/waku-org/nwaku/issues/2995)) ([fd83b42f](https://github.com/waku-org/nwaku/commit/fd83b42f))
+- Better timing and requestId detail for slower store db queries ([#2994](https://github.com/waku-org/nwaku/issues/2994)) ([e8a49b76](https://github.com/waku-org/nwaku/commit/e8a49b76))
+- remove unused setting from external_config.nim ([#3004](https://github.com/waku-org/nwaku/issues/3004)) ([fd84363e](https://github.com/waku-org/nwaku/commit/fd84363e))
+- delivery monitor for store v3 reliability protocol ([#2977](https://github.com/waku-org/nwaku/issues/2977)) ([0f68274c](https://github.com/waku-org/nwaku/commit/0f68274c))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+| [`WAKU-SYNC`](https://github.com/waku-org/specs/blob/feat--waku-sync/standards/core/sync.md) | `draft` | `/vac/waku/sync/1.0.0` |
+
+## v0.32.0 (2024-08-30)
+
+#### Notes:
+
+* A new `discv5-only` CLI flag was introduced, which if set to true will perform optimizations for nodes that only run the DiscV5 service
+* The `protected-topic` CLI config item has been deprecated in favor of the new `protected-shard` configuration. Protected topics are still supported and will be completely removed in two releases time for `v0.34.0`
+
+### Release highlights
+
+* Merged Nwaku Sync protocol for synchronizing store nodes
+* Added Store Resume mechanism to retrieve messages sent when the node was offline
+
+### Features
+
+- Nwaku Sync ([#2403](https://github.com/waku-org/nwaku/issues/2403)) ([2cc86c51](https://github.com/waku-org/nwaku/commit/2cc86c51))
+- misc. updates for discovery network analysis ([#2930](https://github.com/waku-org/nwaku/issues/2930)) ([4340eb75](https://github.com/waku-org/nwaku/commit/4340eb75))
+- store resume ([#2919](https://github.com/waku-org/nwaku/issues/2919)) ([aed2a113](https://github.com/waku-org/nwaku/commit/aed2a113))
+
+### Bug Fixes
+
+- return on insert error ([#2956](https://github.com/waku-org/nwaku/issues/2956)) ([5f0fbd78](https://github.com/waku-org/nwaku/commit/5f0fbd78))
+- network monitor improvements ([#2939](https://github.com/waku-org/nwaku/issues/2939)) ([80583237](https://github.com/waku-org/nwaku/commit/80583237))
+- add back waku discv5 metrics ([#2927](https://github.com/waku-org/nwaku/issues/2927)) ([e4e01fab](https://github.com/waku-org/nwaku/commit/e4e01fab))
+- update and shift unittest ([#2934](https://github.com/waku-org/nwaku/issues/2934)) ([08973add](https://github.com/waku-org/nwaku/commit/08973add))
+- handle rln-relay-message-limit ([#2867](https://github.com/waku-org/nwaku/issues/2867)) ([8d107b0d](https://github.com/waku-org/nwaku/commit/8d107b0d))
+
+### Changes
+
+- libwaku retrieve my enr and adapt golang example ([#2987](https://github.com/waku-org/nwaku/issues/2987)) ([1ff9f1dd](https://github.com/waku-org/nwaku/commit/1ff9f1dd))
+- run `ANALYZE messages` regularly for better db performance ([#2986](https://github.com/waku-org/nwaku/issues/2986)) ([32f2d85d](https://github.com/waku-org/nwaku/commit/32f2d85d))
+- liteprotocoltester for simulation and for fleets ([#2813](https://github.com/waku-org/nwaku/issues/2813)) ([f4fa73e9](https://github.com/waku-org/nwaku/commit/f4fa73e9))
+- lock in nph version and add pre-commit hook ([#2938](https://github.com/waku-org/nwaku/issues/2938)) ([d63e3430](https://github.com/waku-org/nwaku/commit/d63e3430))
+- logging received message info via onValidated observer ([#2973](https://github.com/waku-org/nwaku/issues/2973)) ([e8bce67d](https://github.com/waku-org/nwaku/commit/e8bce67d))
+- deprecating protected topics in favor of protected shards ([#2983](https://github.com/waku-org/nwaku/issues/2983)) ([e51ffe07](https://github.com/waku-org/nwaku/commit/e51ffe07))
+- rename NsPubsubTopic ([#2974](https://github.com/waku-org/nwaku/issues/2974)) ([67439057](https://github.com/waku-org/nwaku/commit/67439057))
+- install dig ([#2975](https://github.com/waku-org/nwaku/issues/2975)) ([d24b56b9](https://github.com/waku-org/nwaku/commit/d24b56b9))
+- print WakuMessageHash as hex strings ([#2969](https://github.com/waku-org/nwaku/issues/2969)) ([2fd4eb62](https://github.com/waku-org/nwaku/commit/2fd4eb62))
+- updating dependencies for release 0.32.0 ([#2971](https://github.com/waku-org/nwaku/issues/2971)) ([dfd42a7c](https://github.com/waku-org/nwaku/commit/dfd42a7c))
+- bump negentropy to latest master ([#2968](https://github.com/waku-org/nwaku/issues/2968)) ([b36cb075](https://github.com/waku-org/nwaku/commit/b36cb075))
+- keystore: verbose error message when credential is not found ([#2943](https://github.com/waku-org/nwaku/issues/2943)) ([0f11ee14](https://github.com/waku-org/nwaku/commit/0f11ee14))
+- upgrade peer exchange mounting ([#2953](https://github.com/waku-org/nwaku/issues/2953)) ([42f1bed0](https://github.com/waku-org/nwaku/commit/42f1bed0))
+- replace statusim.net instances with status.im ([#2941](https://github.com/waku-org/nwaku/issues/2941)) ([f534549a](https://github.com/waku-org/nwaku/commit/f534549a))
+- updating doc reference to https rpc ([#2937](https://github.com/waku-org/nwaku/issues/2937)) ([bb7bba35](https://github.com/waku-org/nwaku/commit/bb7bba35))
+- Simplification of store legacy code ([#2931](https://github.com/waku-org/nwaku/issues/2931)) ([d4e8a0da](https://github.com/waku-org/nwaku/commit/d4e8a0da))
+- add peer filtering by cluster for waku peer exchange ([#2932](https://github.com/waku-org/nwaku/issues/2932)) ([b4618f98](https://github.com/waku-org/nwaku/commit/b4618f98))
+- return all connected peers from REST API ([#2923](https://github.com/waku-org/nwaku/issues/2923)) ([a29eca77](https://github.com/waku-org/nwaku/commit/a29eca77))
+- adding lint job to the CI ([#2925](https://github.com/waku-org/nwaku/issues/2925)) ([086cc8ed](https://github.com/waku-org/nwaku/commit/086cc8ed))
+- improve sonda dashboard ([#2918](https://github.com/waku-org/nwaku/issues/2918)) ([6d385cef](https://github.com/waku-org/nwaku/commit/6d385cef))
+- Add new custom built and test target to make in order to enable easy build or test single nim modules ([#2913](https://github.com/waku-org/nwaku/issues/2913)) ([ad25f437](https://github.com/waku-org/nwaku/commit/ad25f437))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+| [`WAKU-SYNC`](https://github.com/waku-org/specs/blob/feat--waku-sync/standards/core/sync.md) | `draft` | `/vac/waku/sync/1.0.0` |
+
+## v0.31.1 (2024-08-02)
+
+### Changes
+
+- Optimize hash queries with lookup table ([#2933](https://github.com/waku-org/nwaku/issues/2933)) ([6463885bf](https://github.com/waku-org/nwaku/commit/6463885bf))
+
+### Bug fixes
+
+* Use of detach finalize when needed [2966](https://github.com/waku-org/nwaku/pull/2966)
+* Prevent legacy store from creating new partitions as that approach blocked the database.
+[2931](https://github.com/waku-org/nwaku/pull/2931)
+
+* lightpush better feedback in case the lightpush service node does not have peers [2951](https://github.com/waku-org/nwaku/pull/2951)
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`WAKU2-STORE`](https://github.com/waku-org/specs/blob/master/standards/core/store.md) | `draft` | `/vac/waku/store-query/3.0.0` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+## v0.31.0 (2024-07-16)
+### Notes
+
+* Named sharding has been deprecated in favor of static sharding. Topics in formats other than `/waku/2/rs//` are no longer supported
+
+### Features
+
+- DOS protection of non relay protocols - rate limit phase3 (#2897) ([ba418ab5b](https://github.com/waku-org/nwaku/commit/ba418ab5b))
+- sonda tool (#2893) ([e269dca9c](https://github.com/waku-org/nwaku/commit/e269dca9c))
+- add proper per shard bandwidth metric calculation (#2851) ([8f14c0473](https://github.com/waku-org/nwaku/commit/8f14c0473))
+
+### Bug Fixes
+
+- bug(storev3): can't advance cursor [#2745](https://github.com/waku-org/nwaku/issues/2745)
+- chore(storev3): only select the messageHash column when using a store query with include_data: false [#2637](https://github.com/waku-org/nwaku/issues/2637)
+- rln_keystore_generator improve error handling for unrecoverable failure (#2881) ([1c9eb2741](https://github.com/waku-org/nwaku/commit/1c9eb2741))
+- duplicate message forwarding in filter service (#2842) ([99149ea9d](https://github.com/waku-org/nwaku/commit/99149ea9d))
+- only set disconnect time on left event (#2831) ([01050138c](https://github.com/waku-org/nwaku/commit/01050138c))
+- adding peer exchange peers to the peerStore (#2824) ([325e13169](https://github.com/waku-org/nwaku/commit/325e13169))
+- ci use --tags to match non-annotated tags (#2814) ([317c83dc1](https://github.com/waku-org/nwaku/commit/317c83dc1))
+- update peers ENRs in peer store in case they are updated (#2818) ([cda18f96c](https://github.com/waku-org/nwaku/commit/cda18f96c))
+- mount metadata in wakucanary (#2793) ([3b27aee82](https://github.com/waku-org/nwaku/commit/3b27aee82))
+
+### Changes
+
+- setting filter handling logs to trace (#2914) ([5c539fe13](https://github.com/waku-org/nwaku/commit/5c539fe13))
+- enhance postgres and retention policy logs (#2884) ([71ee42de5](https://github.com/waku-org/nwaku/commit/71ee42de5))
+- improving logging under debugDiscv5 flag (#2899) ([8578fb0c3](https://github.com/waku-org/nwaku/commit/8578fb0c3))
+- archive and drivers refactor (#2761) ([f54ba10bc](https://github.com/waku-org/nwaku/commit/f54ba10bc))
+- new release process to include Status fleets (#2825) ([4264666a3](https://github.com/waku-org/nwaku/commit/4264666a3))
+- sqlite make sure code is always run (#2891) ([4ac4ab2a4](https://github.com/waku-org/nwaku/commit/4ac4ab2a4))
+- deprecating named sharding (#2723) ([e1518cf9f](https://github.com/waku-org/nwaku/commit/e1518cf9f))
+- bump dependencies for v0.31.0 (#2885) ([fd6a71cdd](https://github.com/waku-org/nwaku/commit/fd6a71cdd))
+- refactor relative path to better absolute (#2861) ([8bfad3ab4](https://github.com/waku-org/nwaku/commit/8bfad3ab4))
+- saving agent and protoVersion in peerStore (#2860) ([cae0c7e37](https://github.com/waku-org/nwaku/commit/cae0c7e37))
+- unit test for duplicate message push (#2852) ([31c632e42](https://github.com/waku-org/nwaku/commit/31c632e42))
+- remove all pre-nim-1.6 deadcode from codebase (#2857) ([9bd8c33ae](https://github.com/waku-org/nwaku/commit/9bd8c33ae))
+- nim-chronos bump submodule (#2850) ([092add1ca](https://github.com/waku-org/nwaku/commit/092add1ca))
+- ignore arbitrary data stored in `multiaddrs` enr key (#2853) ([76d5b2642](https://github.com/waku-org/nwaku/commit/76d5b2642))
+- add origin to peers admin endpoint (#2848) ([7205f95cf](https://github.com/waku-org/nwaku/commit/7205f95cf))
+- add discv5 logs (#2811) ([974b8a39a](https://github.com/waku-org/nwaku/commit/974b8a39a))
+- archive.nim - increase the max limit of content topics per query to 100 (#2846) ([a05fa0691](https://github.com/waku-org/nwaku/commit/a05fa0691))
+- update content-topic parsing for filter (#2835) ([733edae43](https://github.com/waku-org/nwaku/commit/733edae43))
+- better descriptive log (#2826) ([94947a850](https://github.com/waku-org/nwaku/commit/94947a850))
+- zerokit: bump submodule (#2830) ([c483acee3](https://github.com/waku-org/nwaku/commit/c483acee3))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`WAKU2-STORE`](https://github.com/waku-org/specs/blob/master/standards/core/store.md) | `draft` | `/vac/waku/store-query/3.0.0` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+
+## v0.30.2 (2024-07-12)
+
+### Release highlights
+
+* RLN message limit to 100 messages per epoch.
+* Avoid exclusive access when creating new partitions in the PostgreSQL messages table.
+
+### Changes
+
+- chore(rln): rln message limit to 100 ([#2883](https://github.com/waku-org/nwaku/pull/2883))
+- fix: postgres_driver better partition creation without exclusive access [28bdb70b](https://github.com/waku-org/nwaku/commit/28bdb70be46d3fb3a6f992b3f9f2de1defd85a30)
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+## v0.30.1 (2024-07-03)
+
+### Notes
+
+* Before upgrading to this version, if you are currently using RLN, make sure to remove your existing `keystore` folder and `rln_tree`
+and start your installation from scratch, as
+explained in [nwaku-compose](https://github.com/waku-org/nwaku-compose/blob/1b56575df9ddb904af0941a19ea1df3d36bfddfa/README.md).
+
+### Release highlights
+
+* RLN_v2 is used. The maximum rate can be set to `N` messages per epoch, instead of just one message per epoch. See [this](https://github.com/waku-org/nwaku/issues/2345) for more details. Notice that we established an epoch of 10 minutes.
+
+
+### Changes
+
+- rln-relay: add chain-id flag to wakunode and restrict usage if mismatches rpc provider ([#2858](https://github.com/waku-org/nwaku/pull/2858))
+- rln: fix nullifierlog vulnerability ([#2855](https://github.com/waku-org/nwaku/pull/2855))
+- chore: add TWN parameters for RLNv2 ([#2843](https://github.com/waku-org/nwaku/pull/2843))
+- fix(rln-relay): clear nullifier log only if length is over max epoch gap ([#2836](https://github.com/waku-org/nwaku/pull/2836))
+- rlnv2: clean fork of rlnv2 ([#2828](https://github.com/waku-org/nwaku/issues/2828)) ([a02832fe](https://github.com/waku-org/nwaku/commit/a02832fe))
+- zerokit: bump submodule ([#2830](https://github.com/waku-org/nwaku/issues/2830)) ([bd064882](https://github.com/waku-org/nwaku/commit/bd064882))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+## v0.29.0 (2024-06-19)
+
+## What's Changed
+
+Notes:
+
+* Named sharding will be deprecated in favor of static sharding. Topics in formats other than `/waku/2/rs//` will stop being supported starting from `v0.31.0`
+
+Release highlights:
+
+* Android support in libwaku
+* Discovery is available in libwaku
+* New LiteProcotolTester tool
+* RLN proofs as a lightpush service
+
+### Features
+
+- RLN proofs as a lightpush service ([#2768](https://github.com/waku-org/nwaku/issues/2768)) ([0561e5bd](https://github.com/waku-org/nwaku/commit/0561e5bd))
+- Push newly released nwaku image with latest-release tag ([#2732](https://github.com/waku-org/nwaku/issues/2732)) ([736ce1cb](https://github.com/waku-org/nwaku/commit/736ce1cb))
+- Rln-relay: use arkzkey variant of zerokit ([#2681](https://github.com/waku-org/nwaku/issues/2681)) ([e7b0777d](https://github.com/waku-org/nwaku/commit/e7b0777d))
+
+### Bug Fixes
+
+- Better sync lock in partition creation ([#2783](https://github.com/waku-org/nwaku/issues/2783)) ([8d3bbb1b](https://github.com/waku-org/nwaku/pull/2809/commits/8d3bbb1b4e79b15c8cf18bb91d366e9ec1153301))
+- Multi nat initialization causing dead lock in waku tests + serialize test runs to avoid timing and port occupied issues ([#2799](https://github.com/waku-org/nwaku/issues/2799)) ([5989de88](https://github.com/waku-org/nwaku/commit/5989de88))
+- Increase on chain group manager starting balance ([#2795](https://github.com/waku-org/nwaku/issues/2795)) ([e72bb7e7](https://github.com/waku-org/nwaku/commit/e72bb7e7))
+- More detailed logs to differentiate shards with peers ([#2794](https://github.com/waku-org/nwaku/issues/2794)) ([55a87d21](https://github.com/waku-org/nwaku/commit/55a87d21))
+- waku_archive: only allow a single instance to execute migrations ([#2736](https://github.com/waku-org/nwaku/issues/2736)) ([88b8e186](https://github.com/waku-org/nwaku/commit/88b8e186))
+- Move postgres related tests under linux conditional ([57ecb3e0](https://github.com/waku-org/nwaku/commit/57ecb3e0))
+- Invalid cursor returning messages ([#2724](https://github.com/waku-org/nwaku/issues/2724)) ([a65b13fc](https://github.com/waku-org/nwaku/commit/a65b13fc))
+- Do not print the db url on error ([#2725](https://github.com/waku-org/nwaku/issues/2725)) ([40296f9d](https://github.com/waku-org/nwaku/commit/40296f9d))
+- Use `when` instead of `if` for adding soname on linux ([#2721](https://github.com/waku-org/nwaku/issues/2721)) ([cbaefeb3](https://github.com/waku-org/nwaku/commit/cbaefeb3))
+- Store v3 bug fixes ([#2718](https://github.com/waku-org/nwaku/issues/2718)) ([4a6ec468](https://github.com/waku-org/nwaku/commit/4a6ec468))
+
+
+### Changes
+
+- Set msg_hash logs to notice level ([#2737](https://github.com/waku-org/nwaku/issues/2737)) ([f5d87c5b](https://github.com/waku-org/nwaku/commit/f5d87c5b))
+- Minor enhancements ([#2789](https://github.com/waku-org/nwaku/issues/2789)) ([31bd6d71](https://github.com/waku-org/nwaku/commit/31bd6d71))
+- postgres_driver - acquire/release advisory lock when creating partitions ([#2784](https://github.com/waku-org/nwaku/issues/2784)) ([c5d19c44](https://github.com/waku-org/nwaku/commit/c5d19c44))
+- Setting fail-fast to false in matrixed github actions ([#2787](https://github.com/waku-org/nwaku/issues/2787)) ([005349cc](https://github.com/waku-org/nwaku/commit/005349cc))
+- Simple link refactor ([#2781](https://github.com/waku-org/nwaku/issues/2781)) ([77adfccd](https://github.com/waku-org/nwaku/commit/77adfccd))
+- Improving liteprotocolteseter stats ([#2750](https://github.com/waku-org/nwaku/issues/2750)) ([4c7c8a15](https://github.com/waku-org/nwaku/commit/4c7c8a15))
+- Extract common prefixes into a constant for multiple query ([#2747](https://github.com/waku-org/nwaku/issues/2747)) ([dfc979a8](https://github.com/waku-org/nwaku/commit/dfc979a8))
+- wakucanary: fix fitler protocol, add storev3 ([#2735](https://github.com/waku-org/nwaku/issues/2735)) ([e0079cd0](https://github.com/waku-org/nwaku/commit/e0079cd0))
+- Bump nim-libp2p version ([#2661](https://github.com/waku-org/nwaku/issues/2661)) ([6fbab633](https://github.com/waku-org/nwaku/commit/6fbab633))
+- Link validation process docs to the release process file ([#2714](https://github.com/waku-org/nwaku/issues/2714)) ([ebe69be8](https://github.com/waku-org/nwaku/commit/ebe69be8))
+- Android support ([#2554](https://github.com/waku-org/nwaku/issues/2554)) ([1e2aa57a](https://github.com/waku-org/nwaku/commit/1e2aa57a))
+- Discovery in libwaku ([#2711](https://github.com/waku-org/nwaku/issues/2711)) ([74646848](https://github.com/waku-org/nwaku/commit/74646848))
+- libwaku - allow to properly set the log level in libwaku and unify a little ([#2708](https://github.com/waku-org/nwaku/issues/2708)) ([3faffdbc](https://github.com/waku-org/nwaku/commit/3faffdbc))
+- waku_discv5, peer_manager - add more logs help debug discovery issues ([#2705](https://github.com/waku-org/nwaku/issues/2705)) ([401630ee](https://github.com/waku-org/nwaku/commit/401630ee))
+- Generic change to reduce the number of compilation warnings ([#2696](https://github.com/waku-org/nwaku/issues/2696)) ([78132dc1](https://github.com/waku-org/nwaku/commit/78132dc1))
+
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+## v0.28.1 (2024-05-29)
+
+This patch release fixes the following bug:
+- Store node does not retrieve messages because the meta field is missing in queries.
+
+### Bug Fix
+
+- Commit that fixes the bug [8b42f199](https://github.com/waku-org/nwaku/commit/8b42f199baf4e00794c4cec4d8601c3f6c330a20)
+
+This is a patch release that is fully backwards-compatible with release `v0.28.0`.
+
+It supports the same [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+
+## v0.28.0 (2024-05-22)
+
+## What's Changed
+
+Release highlights:
+
+* Store V3 has been merged
+* Implemented an enhanced and more robust node health check mechanism
+* Introduced the Waku object to libwaku in order to setup a node and its protocols
+
+### Features
+
+- Added message size check before relay for lightpush ([#2695](https://github.com/waku-org/nwaku/issues/2695)) ([9dfdfa27](https://github.com/waku-org/nwaku/commit/9dfdfa27))
+- adding json string support to bindings config ([#2685](https://github.com/waku-org/nwaku/issues/2685)) ([be5471c6](https://github.com/waku-org/nwaku/commit/be5471c6))
+- Added flexible rate limit checks for store, legacy store and lightpush ([#2668](https://github.com/waku-org/nwaku/issues/2668)) ([026d804a](https://github.com/waku-org/nwaku/commit/026d804a))
+- store v3 return pubsub topics ([#2676](https://github.com/waku-org/nwaku/issues/2676)) ([d700006a](https://github.com/waku-org/nwaku/commit/d700006a))
+- supporting meta field in store ([#2609](https://github.com/waku-org/nwaku/issues/2609)) ([a46d4451](https://github.com/waku-org/nwaku/commit/a46d4451))
+- store v3 ([#2431](https://github.com/waku-org/nwaku/issues/2431)) ([0b0fbfad](https://github.com/waku-org/nwaku/commit/0b0fbfad))
+
+### Bug Fixes
+
+- use await instead of waitFor in async tests ([#2690](https://github.com/waku-org/nwaku/issues/2690)) ([a37c9ba9](https://github.com/waku-org/nwaku/commit/a37c9ba9))
+- message cache removal crash ([#2682](https://github.com/waku-org/nwaku/issues/2682)) ([fa26d05f](https://github.com/waku-org/nwaku/commit/fa26d05f))
+- add `meta` to sqlite migration scripts ([#2675](https://github.com/waku-org/nwaku/issues/2675)) ([82f95999](https://github.com/waku-org/nwaku/commit/82f95999))
+- content_script_version_4.nim: migration failed when dropping unexisting constraing ([#2672](https://github.com/waku-org/nwaku/issues/2672)) ([38f8b08c](https://github.com/waku-org/nwaku/commit/38f8b08c))
+- **filter:** log is too large ([#2665](https://github.com/waku-org/nwaku/issues/2665)) ([cee020f2](https://github.com/waku-org/nwaku/commit/cee020f2))
+- issue [#2644](https://github.com/waku-org/nwaku/issues/2644) properly ([#2663](https://github.com/waku-org/nwaku/issues/2663)) ([853ec186](https://github.com/waku-org/nwaku/commit/853ec186))
+- store v3 validate cursor & remove messages ([#2636](https://github.com/waku-org/nwaku/issues/2636)) ([e03d1165](https://github.com/waku-org/nwaku/commit/e03d1165))
+- **waku_keystore:** sigsegv on different appInfo ([#2654](https://github.com/waku-org/nwaku/issues/2654)) ([5dd645cf](https://github.com/waku-org/nwaku/commit/5dd645cf))
+- **rln-relay:** persist metadata every batch during initial sync ([#2649](https://github.com/waku-org/nwaku/issues/2649)) ([a9e19efd](https://github.com/waku-org/nwaku/commit/a9e19efd))
+- handle named sharding in enr ([#2647](https://github.com/waku-org/nwaku/issues/2647)) ([8d1b0834](https://github.com/waku-org/nwaku/commit/8d1b0834))
+- parse shards properly in enr config for non twn ([#2633](https://github.com/waku-org/nwaku/issues/2633)) ([6e6cb298](https://github.com/waku-org/nwaku/commit/6e6cb298))
+- proto field numbers & status desc ([#2632](https://github.com/waku-org/nwaku/issues/2632)) ([843fe217](https://github.com/waku-org/nwaku/commit/843fe217))
+- missing rate limit setting for legacy store protocol ([#2631](https://github.com/waku-org/nwaku/issues/2631)) ([5f65565c](https://github.com/waku-org/nwaku/commit/5f65565c))
+- **rln-relay:** enforce error callback to remove exception raised from retryWrapper ([#2622](https://github.com/waku-org/nwaku/issues/2622)) ([9c9883a6](https://github.com/waku-org/nwaku/commit/9c9883a6))
+- **rln-relay:** increase retries for 1 minute recovery time ([#2614](https://github.com/waku-org/nwaku/issues/2614)) ([1a23700d](https://github.com/waku-org/nwaku/commit/1a23700d))
+- **ci:** unique comment_tag to reference rln version ([#2613](https://github.com/waku-org/nwaku/issues/2613)) ([2c01fa0f](https://github.com/waku-org/nwaku/commit/2c01fa0f))
+- don't use WakuMessageSize in req/resp protocols ([#2601](https://github.com/waku-org/nwaku/issues/2601)) ([e61e4ff9](https://github.com/waku-org/nwaku/commit/e61e4ff9))
+- create options api for cors preflight request ([#2598](https://github.com/waku-org/nwaku/issues/2598)) ([768c61b1](https://github.com/waku-org/nwaku/commit/768c61b1))
+- node restart test issue ([#2576](https://github.com/waku-org/nwaku/issues/2576)) ([4a8e62ac](https://github.com/waku-org/nwaku/commit/4a8e62ac))
+- **doc:** update REST API docs ([#2581](https://github.com/waku-org/nwaku/issues/2581)) ([006d43ae](https://github.com/waku-org/nwaku/commit/006d43ae))
+
+### Changes
+
+- move code from wakunode2 to a more generic place, waku ([#2670](https://github.com/waku-org/nwaku/issues/2670)) ([840e0122](https://github.com/waku-org/nwaku/commit/840e0122))
+- closing ping streams ([#2692](https://github.com/waku-org/nwaku/issues/2692)) ([7d4857ea](https://github.com/waku-org/nwaku/commit/7d4857ea))
+- Postgres enhance get oldest timestamp ([#2687](https://github.com/waku-org/nwaku/issues/2687)) ([8451cf8e](https://github.com/waku-org/nwaku/commit/8451cf8e))
+- **rln-relay:** health check should account for window of roots ([#2664](https://github.com/waku-org/nwaku/issues/2664)) ([6a1af922](https://github.com/waku-org/nwaku/commit/6a1af922))
+- updating TWN bootstrap fleet to waku.sandbox ([#2638](https://github.com/waku-org/nwaku/issues/2638)) ([22f64bbd](https://github.com/waku-org/nwaku/commit/22f64bbd))
+- simplify migration script postgres version_4 ([#2674](https://github.com/waku-org/nwaku/issues/2674)) ([91c85738](https://github.com/waku-org/nwaku/commit/91c85738))
+- big refactor to add waku component in libwaku instead of only waku node ([#2658](https://github.com/waku-org/nwaku/issues/2658)) ([2463527b](https://github.com/waku-org/nwaku/commit/2463527b))
+- simplify app.nim and move discovery items to appropriate modules ([#2657](https://github.com/waku-org/nwaku/issues/2657)) ([404810aa](https://github.com/waku-org/nwaku/commit/404810aa))
+- log enhancement for message reliability analysis ([#2640](https://github.com/waku-org/nwaku/issues/2640)) ([d5e0e4a9](https://github.com/waku-org/nwaku/commit/d5e0e4a9))
+- metrics server. Simplify app.nim module ([#2650](https://github.com/waku-org/nwaku/issues/2650)) ([4a110f65](https://github.com/waku-org/nwaku/commit/4a110f65))
+- change nim-libp2p branch from unstable to master ([#2648](https://github.com/waku-org/nwaku/issues/2648)) ([d09c9c91](https://github.com/waku-org/nwaku/commit/d09c9c91))
+- Enabling to use a full node for lightpush via rest api without lightpush client configured ([#2626](https://github.com/waku-org/nwaku/issues/2626)) ([2a4c0f15](https://github.com/waku-org/nwaku/commit/2a4c0f15))
+- **rln-relay:** resultify rln-relay 1/n ([#2607](https://github.com/waku-org/nwaku/issues/2607)) ([1d7ff288](https://github.com/waku-org/nwaku/commit/1d7ff288))
+- ci.yml - avoid calling brew link libpq --force on macos ([#2627](https://github.com/waku-org/nwaku/issues/2627)) ([05f332ed](https://github.com/waku-org/nwaku/commit/05f332ed))
+- an enhanced version of convenient node health check script ([#2624](https://github.com/waku-org/nwaku/issues/2624)) ([7f8d8e80](https://github.com/waku-org/nwaku/commit/7f8d8e80))
+- **rln-db-inspector:** add more logging to find zero leaf indices ([#2617](https://github.com/waku-org/nwaku/issues/2617)) ([40752b1e](https://github.com/waku-org/nwaku/commit/40752b1e))
+- addition of waku_api/rest/builder.nim and reduce app.nim ([#2623](https://github.com/waku-org/nwaku/issues/2623)) ([b28207ab](https://github.com/waku-org/nwaku/commit/b28207ab))
+- Separation of node health and initialization state from rln_relay ([#2612](https://github.com/waku-org/nwaku/issues/2612)) ([6d135b0d](https://github.com/waku-org/nwaku/commit/6d135b0d))
+- enabling rest api as default ([#2600](https://github.com/waku-org/nwaku/issues/2600)) ([6bc79bc7](https://github.com/waku-org/nwaku/commit/6bc79bc7))
+- move app.nim and networks_config.nim to waku/factory ([#2608](https://github.com/waku-org/nwaku/issues/2608)) ([1ba9df4b](https://github.com/waku-org/nwaku/commit/1ba9df4b))
+- workflow to autoassign PR ([#2604](https://github.com/waku-org/nwaku/issues/2604)) ([10d36c39](https://github.com/waku-org/nwaku/commit/10d36c39))
+- start moving discovery modules to waku/discovery ([#2587](https://github.com/waku-org/nwaku/issues/2587)) ([828583ad](https://github.com/waku-org/nwaku/commit/828583ad))
+- don't create docker images for users without org's secrets ([#2585](https://github.com/waku-org/nwaku/issues/2585)) ([51ec12be](https://github.com/waku-org/nwaku/commit/51ec12be))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## v0.27.0 (2024-04-19)
+
+> **Note:**
+
+> - Filter v1 protocol and its REST-API access have been deprecated.
+> - A new field of the `WakuMetadataRequest` protobuf for shards was introduced. The old shards field (2) will be deprecated in 2 releases time
+> - CLI flags `--requestRateLimit` and `--requestRatePeriod` have been added for rate limiting configuration. Period is measured in seconds. Limits are measured per protocol per period of time. Over limit will result in TOO_MANY_REQUEST (429) response.
+
+## What's Changed
+
+Release highlights:
+
+* Introduced configurable rate limiting for lightpush and store requests
+* Sync time has been considerably reduced for node initialization
+* Significant refactors were made to node initialization and `WakuArchive` logic as work towards C-bindings and Store V3 features
+
+### Features
+
+- Added simple, configurable rate limit for lightpush and store-query ([#2390](https://github.com/waku-org/nwaku/issues/2390)) ([a00f350c](https://github.com/waku-org/nwaku/commit/a00f350c))
+- examples/golang/waku.go add new example ([#2559](https://github.com/waku-org/nwaku/issues/2559)) ([8d66a548](https://github.com/waku-org/nwaku/commit/8d66a548))
+- **c-bindings:** rln relay ([#2544](https://github.com/waku-org/nwaku/issues/2544)) ([2aa835e3](https://github.com/waku-org/nwaku/commit/2aa835e3))
+- **incentivization:** add codec for eligibility proof and status ([#2419](https://github.com/waku-org/nwaku/issues/2419)) ([65530264](https://github.com/waku-org/nwaku/commit/65530264))
+- **rest:** add support to ephemeral field ([#2525](https://github.com/waku-org/nwaku/issues/2525)) ([c734f60d](https://github.com/waku-org/nwaku/commit/c734f60d))
+- archive update for store v3 ([#2451](https://github.com/waku-org/nwaku/issues/2451)) ([505479b8](https://github.com/waku-org/nwaku/commit/505479b8))
+- **c-bindings:** add function to dealloc nodes ([#2499](https://github.com/waku-org/nwaku/issues/2499)) ([8341864d](https://github.com/waku-org/nwaku/commit/8341864d))
+
+### Bug Fixes
+
+- **rln-relay:** reduce sync time ([#2577](https://github.com/waku-org/nwaku/issues/2577)) ([480a62fa](https://github.com/waku-org/nwaku/commit/480a62fa))
+- rest store: content_topic -> contentTopic in the response ([#2584](https://github.com/waku-org/nwaku/issues/2584)) ([d2578553](https://github.com/waku-org/nwaku/commit/d2578553))
+- **c-bindings:** rln credential path key ([#2564](https://github.com/waku-org/nwaku/issues/2564)) ([3d752b11](https://github.com/waku-org/nwaku/commit/3d752b11))
+- cluster-id 0 disc5 issue ([#2562](https://github.com/waku-org/nwaku/issues/2562)) ([a76c9587](https://github.com/waku-org/nwaku/commit/a76c9587))
+- regex for rpc endpoint ([#2563](https://github.com/waku-org/nwaku/issues/2563)) ([c87545d5](https://github.com/waku-org/nwaku/commit/c87545d5))
+- **rln:** set a minimum epoch gap ([#2555](https://github.com/waku-org/nwaku/issues/2555)) ([b5e4795f](https://github.com/waku-org/nwaku/commit/b5e4795f))
+- fix regresion + remove deprecated flag ([#2556](https://github.com/waku-org/nwaku/issues/2556)) ([47ad0fb0](https://github.com/waku-org/nwaku/commit/47ad0fb0))
+- **networkmanager:** regularly disconnect from random peers ([#2553](https://github.com/waku-org/nwaku/issues/2553)) ([70c53fc0](https://github.com/waku-org/nwaku/commit/70c53fc0))
+- remove subscription queue limit ([#2551](https://github.com/waku-org/nwaku/issues/2551)) ([94ff5eab](https://github.com/waku-org/nwaku/commit/94ff5eab))
+- peer_manager - extend the number of connection requests to known peers ([#2534](https://github.com/waku-org/nwaku/issues/2534)) ([2173fe22](https://github.com/waku-org/nwaku/commit/2173fe22))
+- **2491:** Fix metadata protocol disconnecting light nodes ([#2533](https://github.com/waku-org/nwaku/issues/2533)) ([33774fad](https://github.com/waku-org/nwaku/commit/33774fad))
+- **rest:** filter/v2/subscriptions response ([#2529](https://github.com/waku-org/nwaku/issues/2529)) ([7aea2d4f](https://github.com/waku-org/nwaku/commit/7aea2d4f))
+- **store:** retention policy regex ([#2532](https://github.com/waku-org/nwaku/issues/2532)) ([23a291b3](https://github.com/waku-org/nwaku/commit/23a291b3))
+- enable autosharding in any cluster ([#2505](https://github.com/waku-org/nwaku/issues/2505)) ([5a225809](https://github.com/waku-org/nwaku/commit/5a225809))
+- introduce new field for shards in metadata protocol ([#2511](https://github.com/waku-org/nwaku/issues/2511)) ([f9f92b7d](https://github.com/waku-org/nwaku/commit/f9f92b7d))
+- **rln-relay:** handle empty metadata returned by getMetadata proc ([#2516](https://github.com/waku-org/nwaku/issues/2516)) ([1274b15d](https://github.com/waku-org/nwaku/commit/1274b15d))
+
+### Changes
+
+- adding migration script adding i_query index ([#2578](https://github.com/waku-org/nwaku/issues/2578)) ([4117fe65](https://github.com/waku-org/nwaku/commit/4117fe65))
+- bumping chronicles version ([#2583](https://github.com/waku-org/nwaku/issues/2583)) ([a04e0d99](https://github.com/waku-org/nwaku/commit/a04e0d99))
+- add ARM64 support for Linux/MacOS ([#2580](https://github.com/waku-org/nwaku/issues/2580)) ([269139cf](https://github.com/waku-org/nwaku/commit/269139cf))
+- **rln:** update submodule + rln patch version ([#2574](https://github.com/waku-org/nwaku/issues/2574)) ([24f6fed8](https://github.com/waku-org/nwaku/commit/24f6fed8))
+- bumping dependencies for 0.27.0 ([#2572](https://github.com/waku-org/nwaku/issues/2572)) ([f68ac792](https://github.com/waku-org/nwaku/commit/f68ac792))
+- **c-bindings:** node initialization ([#2547](https://github.com/waku-org/nwaku/issues/2547)) ([6d0f6d82](https://github.com/waku-org/nwaku/commit/6d0f6d82))
+- remove deprecated legacy filter protocol ([#2507](https://github.com/waku-org/nwaku/issues/2507)) ([e8613172](https://github.com/waku-org/nwaku/commit/e8613172))
+- switch wakuv2 to waku fleet ([#2519](https://github.com/waku-org/nwaku/issues/2519)) ([18a05359](https://github.com/waku-org/nwaku/commit/18a05359))
+- create nph.md ([#2536](https://github.com/waku-org/nwaku/issues/2536)) ([a576e624](https://github.com/waku-org/nwaku/commit/a576e624))
+- Better postgres duplicate insert ([#2535](https://github.com/waku-org/nwaku/issues/2535)) ([693a1778](https://github.com/waku-org/nwaku/commit/693a1778))
+- add 150 kB to msg size histogram metric ([#2430](https://github.com/waku-org/nwaku/issues/2430)) ([2c1391d3](https://github.com/waku-org/nwaku/commit/2c1391d3))
+- content_script_version_2: add simple protection and rename messages_backup if exists ([#2531](https://github.com/waku-org/nwaku/issues/2531)) ([c6c376b5](https://github.com/waku-org/nwaku/commit/c6c376b5))
+- **vendor:** update nim-libp2p path ([#2527](https://github.com/waku-org/nwaku/issues/2527)) ([3c823756](https://github.com/waku-org/nwaku/commit/3c823756))
+- adding node factory tests ([#2524](https://github.com/waku-org/nwaku/issues/2524)) ([a1b3e090](https://github.com/waku-org/nwaku/commit/a1b3e090))
+- factory cleanup ([#2523](https://github.com/waku-org/nwaku/issues/2523)) ([8d7eb3a6](https://github.com/waku-org/nwaku/commit/8d7eb3a6))
+- **rln-relay-v2:** wakunode testing + improvements ([#2501](https://github.com/waku-org/nwaku/issues/2501)) ([059cb975](https://github.com/waku-org/nwaku/commit/059cb975))
+- update CHANGELOG for v0.26.0 release ([#2518](https://github.com/waku-org/nwaku/issues/2518)) ([097cb362](https://github.com/waku-org/nwaku/commit/097cb362))
+- migrating logic from wakunode2.nim to node_factory.nim ([#2504](https://github.com/waku-org/nwaku/issues/2504)) ([dcc88ee0](https://github.com/waku-org/nwaku/commit/dcc88ee0))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## v0.26.0 (2024-03-07)
+
+> **Note:**
+> - JSON-RPC API has been removed completely. Instead we recommend you to utilize REST API endpoints that have same and extended functionality.
+> Please have a look at Waku's REST-API reference: https://waku-org.github.io/waku-rest-api
+> - Support for Cross-Origin-Resource-Sharing (CORS headers) is added for our REST-API services. This allows you to access our REST-API from a browser.
+> New repeatable CLI flag is added by this feature:
+> `--rest-allow-origin="example.com"` or `--rest-allow-origin="127.0.0.0:*"`
+> Flag allows using wildcards (`*` and `?`) in the origin string.
+> - Store protocol now has a better support for controlling DB size of Postgres store. This feature needs no user action.
+
+> **Announcement:**
+>
+> Please notice that from the next release (0.27.0) we will deprecate features.
+>
+> - We will decomission the Filter v1 protocol and its REST-API access.
+
+### Features
+
+- Postgres partition implementation ([#2506](https://github.com/waku-org/nwaku/issues/2506)) ([161a10ec](https://github.com/waku-org/nwaku/commit/161a10ec))
+- **waku-stealth-commitments:** waku stealth commitment protocol ([#2490](https://github.com/waku-org/nwaku/issues/2490)) ([0def4904](https://github.com/waku-org/nwaku/commit/0def4904))
+- **bindings:** generate a random private key ([#2446](https://github.com/waku-org/nwaku/issues/2446)) ([56ff30ca](https://github.com/waku-org/nwaku/commit/56ff30ca))
+- prioritise yamux above mplex ([#2417](https://github.com/waku-org/nwaku/issues/2417)) ([ce151efc](https://github.com/waku-org/nwaku/commit/ce151efc))
+- supporting meta field in WakuMessage ([#2384](https://github.com/waku-org/nwaku/issues/2384)) ([3903f130](https://github.com/waku-org/nwaku/commit/3903f130))
+- `eventCallback` per wakunode and `userData` ([#2418](https://github.com/waku-org/nwaku/issues/2418)) ([707f3e8b](https://github.com/waku-org/nwaku/commit/707f3e8b))
+- **rln-relay-v2:** nonce/messageId manager ([#2413](https://github.com/waku-org/nwaku/issues/2413)) ([50308eda](https://github.com/waku-org/nwaku/commit/50308eda))
+- **networkmonitor:** add support for rln ([#2401](https://github.com/waku-org/nwaku/issues/2401)) ([9c0e9431](https://github.com/waku-org/nwaku/commit/9c0e9431))
+- **rln-relay-v2:** rln-keystore-generator updates ([#2392](https://github.com/waku-org/nwaku/issues/2392)) ([2d46c351](https://github.com/waku-org/nwaku/commit/2d46c351))
+- add yamux support ([#2397](https://github.com/waku-org/nwaku/issues/2397)) ([1b402667](https://github.com/waku-org/nwaku/commit/1b402667))
+
+### Bug Fixes
+
+- **rln-relay:** make nullifier log abide by epoch ordering ([#2508](https://github.com/waku-org/nwaku/issues/2508)) ([beba14dc](https://github.com/waku-org/nwaku/commit/beba14dc))
+- **postgres:** import under feature flag ([#2500](https://github.com/waku-org/nwaku/issues/2500)) ([e692edf6](https://github.com/waku-org/nwaku/commit/e692edf6))
+- notify Waku Metadata when Waku Filter subscribe to a topic ([#2493](https://github.com/waku-org/nwaku/issues/2493)) ([91e3f8cd](https://github.com/waku-org/nwaku/commit/91e3f8cd))
+- time on 32 bits architecture ([#2492](https://github.com/waku-org/nwaku/issues/2492)) ([0a751228](https://github.com/waku-org/nwaku/commit/0a751228))
+- return message id on `waku_relay_publish` ([#2485](https://github.com/waku-org/nwaku/issues/2485)) ([045091a9](https://github.com/waku-org/nwaku/commit/045091a9))
+- **bindings:** base64 payload and key for content topic ([#2435](https://github.com/waku-org/nwaku/issues/2435)) ([d01585e9](https://github.com/waku-org/nwaku/commit/d01585e9))
+- **rln-relay:** regex pattern match for extended domains ([#2444](https://github.com/waku-org/nwaku/issues/2444)) ([29b0c0b8](https://github.com/waku-org/nwaku/commit/29b0c0b8))
+- checking for keystore file existence ([#2427](https://github.com/waku-org/nwaku/issues/2427)) ([8f487a21](https://github.com/waku-org/nwaku/commit/8f487a21))
+- **rln-relay:** graceful shutdown with non-zero exit code ([#2429](https://github.com/waku-org/nwaku/issues/2429)) ([22026b7e](https://github.com/waku-org/nwaku/commit/22026b7e))
+- check max message size in validator according to configured value ([#2424](https://github.com/waku-org/nwaku/issues/2424)) ([731dfcbd](https://github.com/waku-org/nwaku/commit/731dfcbd))
+- **wakunode2:** move node config inside app init branch ([#2423](https://github.com/waku-org/nwaku/issues/2423)) ([0dac9f9d](https://github.com/waku-org/nwaku/commit/0dac9f9d))
+
+### Changes
+
+- **rln_db_inspector:** include in wakunode2 binary ([#2292](https://github.com/waku-org/nwaku/issues/2292)) ([a9d0e481](https://github.com/waku-org/nwaku/commit/a9d0e481))
+- Update link to DNS discovery tutorial ([#2496](https://github.com/waku-org/nwaku/issues/2496)) ([9ef2eccb](https://github.com/waku-org/nwaku/commit/9ef2eccb))
+- **rln-relay-v2:** added tests for static rln-relay-v2 ([#2484](https://github.com/waku-org/nwaku/issues/2484)) ([5b174fb3](https://github.com/waku-org/nwaku/commit/5b174fb3))
+- moving node initialization code to node_factory.nim ([#2479](https://github.com/waku-org/nwaku/issues/2479)) ([361fe2cd](https://github.com/waku-org/nwaku/commit/361fe2cd))
+- Postgres migrations ([#2477](https://github.com/waku-org/nwaku/issues/2477)) ([560f949a](https://github.com/waku-org/nwaku/commit/560f949a))
+- **rln-relay-v2:** added tests for onchain rln-relay-v2 ([#2482](https://github.com/waku-org/nwaku/issues/2482)) ([88ff9282](https://github.com/waku-org/nwaku/commit/88ff9282))
+- remove json rpc ([#2416](https://github.com/waku-org/nwaku/issues/2416)) ([c994ee04](https://github.com/waku-org/nwaku/commit/c994ee04))
+- **ci:** use git describe for image version ([55ff6674](https://github.com/waku-org/nwaku/commit/55ff6674))
+- Implemented CORS handling for nwaku REST server ([#2470](https://github.com/waku-org/nwaku/issues/2470)) ([d832f92a](https://github.com/waku-org/nwaku/commit/d832f92a))
+- remove rln epoch hardcoding ([#2483](https://github.com/waku-org/nwaku/issues/2483)) ([3f4f6d7e](https://github.com/waku-org/nwaku/commit/3f4f6d7e))
+- **cbindings:** cbindings rust simple libwaku integration example ([#2089](https://github.com/waku-org/nwaku/issues/2089)) ([a4993005](https://github.com/waku-org/nwaku/commit/a4993005))
+- adding NIMFLAGS usage to readme ([#2469](https://github.com/waku-org/nwaku/issues/2469)) ([a1d5cbd9](https://github.com/waku-org/nwaku/commit/a1d5cbd9))
+- bumping nim-libp2p after yamux timeout fix ([#2468](https://github.com/waku-org/nwaku/issues/2468)) ([216531b0](https://github.com/waku-org/nwaku/commit/216531b0))
+- new proc to foster different size retention policy implementations ([#2463](https://github.com/waku-org/nwaku/issues/2463)) ([d5305282](https://github.com/waku-org/nwaku/commit/d5305282))
+- **rln-relay:** use anvil instead of ganache in onchain tests ([#2449](https://github.com/waku-org/nwaku/issues/2449)) ([f6332ac6](https://github.com/waku-org/nwaku/commit/f6332ac6))
+- bindings return multiaddress array ([#2461](https://github.com/waku-org/nwaku/issues/2461)) ([7aea145e](https://github.com/waku-org/nwaku/commit/7aea145e))
+- **ci:** fix IMAGE_NAME to use harbor.status.im ([b700d046](https://github.com/waku-org/nwaku/commit/b700d046))
+- **rln-relay:** remove wss support from node config ([#2442](https://github.com/waku-org/nwaku/issues/2442)) ([2060cfab](https://github.com/waku-org/nwaku/commit/2060cfab))
+- **ci:** reuse discord send function from library ([1151d50f](https://github.com/waku-org/nwaku/commit/1151d50f))
+- **rln-relay-v2:** add tests for serde ([#2421](https://github.com/waku-org/nwaku/issues/2421)) ([d0377056](https://github.com/waku-org/nwaku/commit/d0377056))
+- add stdef.h to libwaku.h ([#2409](https://github.com/waku-org/nwaku/issues/2409)) ([d58aca01](https://github.com/waku-org/nwaku/commit/d58aca01))
+- automatically generating certs if not provided (Waku Canary) ([#2408](https://github.com/waku-org/nwaku/issues/2408)) ([849d76d6](https://github.com/waku-org/nwaku/commit/849d76d6))
+- Simplify configuration for the waku network ([#2404](https://github.com/waku-org/nwaku/issues/2404)) ([985d092f](https://github.com/waku-org/nwaku/commit/985d092f))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## v0.25.0 (2024-02-06)
+
+> **Note:**
+> Waku Filter v2 now has three additional configuration options
+> `--filter-max-peers-to-serve=1000` drives how many peers can subscribe at once and
+> `--filter-max-criteria=1000` defines what is the maximum criterion stored per each peers
+>
+> This release introduces a major change in Filter v2 protocol subscription management.
+> From now each subscribed peer needs to refresh its living subscriptions by sending a SUBSCRIBER_PING message every 5 minutes by default, otherwise the peer's subscription will be removed.
+> `--filter-subscription-timeout=300` defines configurable timeout for the subscriptions (*in seconds*).
+>
+> New experimental feature, shard aware peer manager for relay protocol can be activated by the flag:
+> `--relay-shard-manager=true|false`
+> It is disabled by default.
+
+> **Announcement:**
+>
+> Please notice that from the next release (0.26.0) we will deprecate features.
+>
+> - JSON-RPC API will be removed completely. Instead we recommend you to utilize REST API endpoints that have same and extended functionality.
+> - We will retire websockets support for RLN on-chain group management. You are expected to use HTTP version of ETH_CLIENT_ADDRESS
+
+### Features
+
+- running validators in /relay/v1/auto/messages/{topic} ([#2394](https://github.com/waku-org/nwaku/issues/2394)) ([e4e147bc](https://github.com/waku-org/nwaku/commit/e4e147bc))
+- **rln-relay-v2:** update C FFI api's and serde ([#2385](https://github.com/waku-org/nwaku/issues/2385)) ([b88facd0](https://github.com/waku-org/nwaku/commit/b88facd0))
+- running validators in /relay/v1/messages/{pubsubTopic} ([#2373](https://github.com/waku-org/nwaku/issues/2373)) ([59d8b620](https://github.com/waku-org/nwaku/commit/59d8b620))
+- shard aware relay peer management ([#2332](https://github.com/waku-org/nwaku/issues/2332)) ([edca1df1](https://github.com/waku-org/nwaku/commit/edca1df1))
+
+### Bug Fixes
+
+- adding rln validator as default ([#2367](https://github.com/waku-org/nwaku/issues/2367)) ([bb58a63a](https://github.com/waku-org/nwaku/commit/bb58a63a))
+- Fix test for filter client receiving messages after restart ([#2360](https://github.com/waku-org/nwaku/issues/2360)) ([7de91d92](https://github.com/waku-org/nwaku/commit/7de91d92))
+- making filter admin data test order independent ([#2355](https://github.com/waku-org/nwaku/issues/2355)) ([8a9fad29](https://github.com/waku-org/nwaku/commit/8a9fad29))
+
+### Changes
+
+- **rln-relay-v2:** use rln-v2 contract code ([#2381](https://github.com/waku-org/nwaku/issues/2381)) ([c55ca067](https://github.com/waku-org/nwaku/commit/c55ca067))
+- v0.25 vendor bump and associated fixes ([#2352](https://github.com/waku-org/nwaku/issues/2352)) ([761ce7b1](https://github.com/waku-org/nwaku/commit/761ce7b1))
+- handle errors w.r.t. configured cluster-id and pubsub topics ([#2368](https://github.com/waku-org/nwaku/issues/2368)) ([e04e35e2](https://github.com/waku-org/nwaku/commit/e04e35e2))
+- add coverage target to Makefile ([#2382](https://github.com/waku-org/nwaku/issues/2382)) ([57378873](https://github.com/waku-org/nwaku/commit/57378873))
+- Add check spell allowed words ([#2383](https://github.com/waku-org/nwaku/issues/2383)) ([c1121dd1](https://github.com/waku-org/nwaku/commit/c1121dd1))
+- adding nwaku compose image update to release process ([#2370](https://github.com/waku-org/nwaku/issues/2370)) ([4f06dcff](https://github.com/waku-org/nwaku/commit/4f06dcff))
+- changing digest and hash log format from bytes to hex ([#2363](https://github.com/waku-org/nwaku/issues/2363)) ([025c6ec9](https://github.com/waku-org/nwaku/commit/025c6ec9))
+- log messageHash for lightpush request that helps in debugging ([#2366](https://github.com/waku-org/nwaku/issues/2366)) ([42204115](https://github.com/waku-org/nwaku/commit/42204115))
+- **rln-relay:** enabled http based polling in OnchainGroupManager ([#2364](https://github.com/waku-org/nwaku/issues/2364)) ([efdc5244](https://github.com/waku-org/nwaku/commit/efdc5244))
+- improve POST /relay/v1/auto/messages/{topic} error handling ([#2339](https://github.com/waku-org/nwaku/issues/2339)) ([f841454e](https://github.com/waku-org/nwaku/commit/f841454e))
+- Refactor of FilterV2 subscription management with Time-to-live maintenance ([#2341](https://github.com/waku-org/nwaku/issues/2341)) ([c3358409](https://github.com/waku-org/nwaku/commit/c3358409))
+- Bump `nim-dnsdisc` ([#2354](https://github.com/waku-org/nwaku/issues/2354)) ([3d816c08](https://github.com/waku-org/nwaku/commit/3d816c08))
+- postgres-adoption.md add metadata title, description, and better first-readable-title ([#2346](https://github.com/waku-org/nwaku/issues/2346)) ([2f8e8bcb](https://github.com/waku-org/nwaku/commit/2f8e8bcb))
+- fix typo ([#2348](https://github.com/waku-org/nwaku/issues/2348)) ([a4a8dee3](https://github.com/waku-org/nwaku/commit/a4a8dee3))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## v0.24.0 (2024-01-10)
+
+> Note: The Waku message size limit (150 KiB) is now enforced according to the specifications. To change this limit please use `--max-msg-size="1MiB"`
+
+> Note: `--ip-colocation-limit=2` is the new parameter for limiting connections from the same IP
+
+## What's Changed
+
+Release highlights:
+* IP colocation filter can now be changed via a configuration parameter.
+* New filter admin endpoint can now be used to access subscription data.
+* Waku message size limit can now be changed via a configuration parameter.
+
+### Features
+
+- feat: adding filter data admin endpoint (REST) [#2314](https://github.com/waku-org/nwaku/pull/2314)
+- ip colocation is parameterizable. if set to 0, it is disabled [#2323](https://github.com/waku-org/nwaku/pull/2323)
+
+### Bug Fixes
+- fix: revert "feat: shard aware peer management [#2151](https://github.com/waku-org/nwaku/pull/2151)" [#2312](https://github.com/waku-org/nwaku/pull/2312)
+- fix: setting connectivity loop interval to 15 seconds [#2307](https://github.com/waku-org/nwaku/pull/2307)
+- fix: set record to the Waku node builder in the examples as it is required [#2328](https://github.com/waku-org/nwaku/pull/2328)
+- fix(discv5): add bootnode filter exception [#2267](https://github.com/waku-org/nwaku/pull/2267)
+
+
+### Changes
+- update CHANGELOG.md for 0.23.0 [#2309](https://github.com/waku-org/nwaku/pull/2309)
+- test(store): Implement store tests [#2235](https://github.com/waku-org/nwaku/pull/2235), [#2240](https://github.com/waku-org/nwaku/commit/86353e22a871820c132deee077f65e7af4356671)
+- refactor(store): HistoryQuery.direction [#2263](https://github.com/waku-org/nwaku/pull/2263)
+- test_driver_postgres: enhance test coverage, multiple and single topic [#2301](https://github.com/waku-org/nwaku/pull/2301)
+- chore: examples/nodejs - adapt code to latest callback and ctx/userData definitions [#2281](https://github.com/waku-org/nwaku/pull/2281)
+- chore: update `CHANGELOG.md` to reflect bug fix for issue [#2317](https://github.com/waku-org/nwaku/issues/2317) [#2340](https://github.com/waku-org/nwaku/pull/2340) in v0.23.1
+- test(peer-connection-managenent): functional tests [#2321](https://github.com/waku-org/nwaku/pull/2321)
+- docs: update post-release steps [#2336](https://github.com/waku-org/nwaku/pull/2336)
+- docs: fix typos across various documentation files [#2310](https://github.com/waku-org/nwaku/pull/2310)
+- test(peer-connection-managenent): functional tests [#2321](https://github.com/waku-org/nwaku/pull/2321)
+- bump vendors for 0.24.0 [#2333](https://github.com/waku-org/nwaku/pull/2333)
+- test(autosharding): functional tests [#2318](https://github.com/waku-org/nwaku/pull/2318)
+- docs: add benchmark around postgres adoption [#2316](https://github.com/waku-org/nwaku/pull/2316)
+- chore: set max Waku message size to 150KiB according to spec [#2298](https://github.com/waku-org/nwaku/pull/2298)
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## v0.23.1 (2023-01-09)
+
+This patch release fixes the following bug:
+- Sort order ignored in store nodes.
+
+### Bug Fix
+
+- Bug definition: [#2317](https://github.com/waku-org/nwaku/issues/2317)
+- Commit that fixes the bug [fae20bff](https://github.com/waku-org/nwaku/commit/fae20bff)
+
+This is a patch release that is fully backwards-compatible with release `v0.23.0`.
+
+It supports the same [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+
+## v0.23.0 (2023-12-18)
+
+## What's Changed
+
+Release highlights:
+* Bug fix in Postgres when querying more than one content topic.
+* :warning: Add new DB column `messageHash`. This requires a manual database update in _Postgres_.
+* Updated deterministic message hash algorithm.
+* REST admin can inform whether a node supports lightpush and/or filter protocols.
+* Improvements to cluster id and shards setup.
+* Properly apply RLN when publishing from REST or jsonrpc API.
+* Remove trailing commas from the RLN keystore json generated during credentials registration.
+* General test cleanup, better relay tests and new filter unsubscribe tests.
+* Rewrite docs for clarity and update screenshots.
+
+### Features
+
+- setting image deployment to harbor registry ([93dd5ae5](https://github.com/waku-org/nwaku/commit/93dd5ae5))
+- Add new DB column `messageHash` ([#2202](https://github.com/waku-org/nwaku/issues/2202)) ([aeb77a3e](https://github.com/waku-org/nwaku/commit/aeb77a3e))
+
+### Bug Fixes
+
+- make rln rate limit spec compliant ([#2294](https://github.com/waku-org/nwaku/issues/2294)) ([5847f49d](https://github.com/waku-org/nwaku/commit/5847f49d))
+- update num-msgs archive metrics every minute and not only at the beginning ([#2287](https://github.com/waku-org/nwaku/issues/2287)) ([0fc617ff](https://github.com/waku-org/nwaku/commit/0fc617ff))
+- **rln-relay:** graceful retries on rpc calls ([#2250](https://github.com/waku-org/nwaku/issues/2250)) ([15c1f974](https://github.com/waku-org/nwaku/commit/15c1f974))
+- add protection in rest service to always publish with timestamp if user doesn't provide it ([#2261](https://github.com/waku-org/nwaku/issues/2261)) ([42f19579](https://github.com/waku-org/nwaku/commit/42f19579))
+- remove trailing commas from keystore json ([#2200](https://github.com/waku-org/nwaku/issues/2200)) ([103d3981](https://github.com/waku-org/nwaku/commit/103d3981))
+- **dockerfile:** update dockerignore and base image ([#2262](https://github.com/waku-org/nwaku/issues/2262)) ([c86dc442](https://github.com/waku-org/nwaku/commit/c86dc442))
+- waku_filter_v2/common: PEER_DIAL_FAILURE ret code change: 200 -> 504 ([#2236](https://github.com/waku-org/nwaku/issues/2236)) ([6301bec0](https://github.com/waku-org/nwaku/commit/6301bec0))
+- extended Postgres code to support retention policy + refactoring ([#2244](https://github.com/waku-org/nwaku/issues/2244)) ([a1ed517f](https://github.com/waku-org/nwaku/commit/a1ed517f))
+- admin REST API to be enabled only if config is set ([#2218](https://github.com/waku-org/nwaku/issues/2218)) ([110de90f](https://github.com/waku-org/nwaku/commit/110de90f))
+- **rln:** error in api when rate limit ([#2212](https://github.com/waku-org/nwaku/issues/2212)) ([51f36099](https://github.com/waku-org/nwaku/commit/51f36099))
+- **relay:** Failing protocol tests ([#2224](https://github.com/waku-org/nwaku/issues/2224)) ([c9e869fb](https://github.com/waku-org/nwaku/commit/c9e869fb))
+- **tests:** Compilation failure fix ([#2222](https://github.com/waku-org/nwaku/issues/2222)) ([a5da1fc4](https://github.com/waku-org/nwaku/commit/a5da1fc4))
+- **rest:** properly check if rln is used ([#2205](https://github.com/waku-org/nwaku/issues/2205)) ([2cb0989a](https://github.com/waku-org/nwaku/commit/2cb0989a))
+
+### Changes
+
+- archive - move error to trace level when insert row fails ([#2283](https://github.com/waku-org/nwaku/issues/2283)) ([574cdf55](https://github.com/waku-org/nwaku/commit/574cdf55))
+- including content topics on FilterSubscribeRequest logs ([#2295](https://github.com/waku-org/nwaku/issues/2295)) ([306c8a62](https://github.com/waku-org/nwaku/commit/306c8a62))
+- vendor bump for 0.23.0 ([#2274](https://github.com/waku-org/nwaku/issues/2274)) ([385daf16](https://github.com/waku-org/nwaku/commit/385daf16))
+- peer_manager.nim - reduce logs from debug to trace ([#2279](https://github.com/waku-org/nwaku/issues/2279)) ([0cc0c805](https://github.com/waku-org/nwaku/commit/0cc0c805))
+- Cbindings allow mounting the Store protocol from libwaku ([#2276](https://github.com/waku-org/nwaku/issues/2276)) ([28142f40](https://github.com/waku-org/nwaku/commit/28142f40))
+- Better feedback invalid content topic ([#2254](https://github.com/waku-org/nwaku/issues/2254)) ([72a1f8c7](https://github.com/waku-org/nwaku/commit/72a1f8c7))
+- fix typos ([#2239](https://github.com/waku-org/nwaku/issues/2239)) ([958b9bd7](https://github.com/waku-org/nwaku/commit/958b9bd7))
+- creating prepare_release template ([#2225](https://github.com/waku-org/nwaku/issues/2225)) ([5883dbeb](https://github.com/waku-org/nwaku/commit/5883dbeb))
+- **rest:** refactor message cache ([#2221](https://github.com/waku-org/nwaku/issues/2221)) ([bebaa59c](https://github.com/waku-org/nwaku/commit/bebaa59c))
+- updating nim-json-serialization dependency ([#2248](https://github.com/waku-org/nwaku/issues/2248)) ([9f4e6f45](https://github.com/waku-org/nwaku/commit/9f4e6f45))
+- **store-archive:** Remove duplicated code ([#2234](https://github.com/waku-org/nwaku/issues/2234)) ([38e100e9](https://github.com/waku-org/nwaku/commit/38e100e9))
+- refactoring peer storage ([#2243](https://github.com/waku-org/nwaku/issues/2243)) ([c301e880](https://github.com/waku-org/nwaku/commit/c301e880))
+- postres driver allow setting the max number of connection from a parameter ([#2246](https://github.com/waku-org/nwaku/issues/2246)) ([b31c1823](https://github.com/waku-org/nwaku/commit/b31c1823))
+- deterministic message hash algorithm updated ([#2233](https://github.com/waku-org/nwaku/issues/2233)) ([a22ee604](https://github.com/waku-org/nwaku/commit/a22ee604))
+- **REST:** returning lightpush support and updated filter protocol ([#2219](https://github.com/waku-org/nwaku/issues/2219)) ([59ee3c69](https://github.com/waku-org/nwaku/commit/59ee3c69))
+- mics. improvements to cluster id and shards setup ([#2187](https://github.com/waku-org/nwaku/issues/2187)) ([897f4879](https://github.com/waku-org/nwaku/commit/897f4879))
+- update docs for rln-keystore-generator ([#2210](https://github.com/waku-org/nwaku/issues/2210)) ([8c5666d2](https://github.com/waku-org/nwaku/commit/8c5666d2))
+- removing automatic vacuuming from retention policy code ([#2228](https://github.com/waku-org/nwaku/issues/2228)) ([9ff441ab](https://github.com/waku-org/nwaku/commit/9ff441ab))
+- decoupling announced and listen addresses ([#2203](https://github.com/waku-org/nwaku/issues/2203)) ([ef8ffbdb](https://github.com/waku-org/nwaku/commit/ef8ffbdb))
+- **release:** update changelog for v0.22.0 release ([#2216](https://github.com/waku-org/nwaku/issues/2216)) ([9c4fdac6](https://github.com/waku-org/nwaku/commit/9c4fdac6))
+- Allow text/plain content type descriptor for json formatted content body ([#2209](https://github.com/waku-org/nwaku/issues/2209)) ([6d81e384](https://github.com/waku-org/nwaku/commit/6d81e384))
+- rewrite for clarity, update screenshots ([#2206](https://github.com/waku-org/nwaku/issues/2206)) ([a0ef3c2f](https://github.com/waku-org/nwaku/commit/a0ef3c2f))
+- **release:** update changelog for v0.21.3 release ([#2208](https://github.com/waku-org/nwaku/issues/2208)) ([f74474b4](https://github.com/waku-org/nwaku/commit/f74474b4))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## v0.22.0 (2023-11-15)
+
+> Note: The `--topic` option is now deprecated in favor of a more specific options `--pubsub-topic` & `--content-topic`
+
+> Note: The `--ext-multiaddr-only` CLI flag was introduced for cases in which the user wants to manually set their announced addresses
+
+## What's Changed
+
+Release highlights:
+* simplified the process of generating RLN credentials through the new `generateRlnKeystore` subcommand
+* added support for configuration of port 0 in order to bind to kernel selected ports
+* shards are now automatically updated in metadata protocol when supported shards change on runtime
+* introduced `messageHash` attribute to SQLite which will later replace the `id` attribute
+
+### Features
+
+- rln-keystore-generator is now a subcommand ([#2189](https://github.com/waku-org/nwaku/issues/2189)) ([3498a846](https://github.com/waku-org/nwaku/commit/3498a846))
+- amending computeDigest func. + related test cases ([#2132](https://github.com/waku-org/nwaku/issues/2132))" ([#2180](https://github.com/waku-org/nwaku/issues/2180)) ([d7ef3ca1](https://github.com/waku-org/nwaku/commit/d7ef3ca1))
+- **discv5:** filter out peers without any listed capability ([#2186](https://github.com/waku-org/nwaku/issues/2186)) ([200a11da](https://github.com/waku-org/nwaku/commit/200a11da))
+- metadata protocol shard subscription ([#2149](https://github.com/waku-org/nwaku/issues/2149)) ([bcf8e963](https://github.com/waku-org/nwaku/commit/bcf8e963))
+- REST APIs discovery handlers ([#2109](https://github.com/waku-org/nwaku/issues/2109)) ([7ca516a5](https://github.com/waku-org/nwaku/commit/7ca516a5))
+- implementing port 0 support ([#2125](https://github.com/waku-org/nwaku/issues/2125)) ([f7b9afc2](https://github.com/waku-org/nwaku/commit/f7b9afc2))
+- messageHash attribute added in SQLite + testcase ([#2142](https://github.com/waku-org/nwaku/issues/2142))" ([#2154](https://github.com/waku-org/nwaku/issues/2154)) ([13aeebe4](https://github.com/waku-org/nwaku/commit/13aeebe4))
+- messageHash attribute added in SQLite + testcase ([#2142](https://github.com/waku-org/nwaku/issues/2142)) ([9cd8c73d](https://github.com/waku-org/nwaku/commit/9cd8c73d))
+- amending computeDigest func. + related test cases ([#2132](https://github.com/waku-org/nwaku/issues/2132)) ([1669f710](https://github.com/waku-org/nwaku/commit/1669f710))
+
+### Bug Fixes
+
+- typo ([6dd28063](https://github.com/waku-org/nwaku/commit/6dd28063))
+- lightpush rest ([#2176](https://github.com/waku-org/nwaku/issues/2176)) ([fa467e24](https://github.com/waku-org/nwaku/commit/fa467e24))
+- **ci:** fix Docker tag for latest and release jobs ([52759faa](https://github.com/waku-org/nwaku/commit/52759faa))
+- **rest:** fix bug in rest api when sending rln message ([#2169](https://github.com/waku-org/nwaku/issues/2169)) ([250e8b98](https://github.com/waku-org/nwaku/commit/250e8b98))
+- updating v0.21.1 release date in changelog ([#2160](https://github.com/waku-org/nwaku/issues/2160)) ([3be61636](https://github.com/waku-org/nwaku/commit/3be61636))
+
+### Changes
+
+- Optimize postgres - prepared statements in select ([#2182](https://github.com/waku-org/nwaku/issues/2182)) ([6da1aeec](https://github.com/waku-org/nwaku/commit/6da1aeec))
+- **release:** update changelog for v0.21.2 release ([#2188](https://github.com/waku-org/nwaku/issues/2188)) ([d0a93e7c](https://github.com/waku-org/nwaku/commit/d0a93e7c))
+- upgrade dependencies v0.22 ([#2185](https://github.com/waku-org/nwaku/issues/2185)) ([b9563ae0](https://github.com/waku-org/nwaku/commit/b9563ae0))
+- Optimize postgres - use of rowCallback approach ([#2171](https://github.com/waku-org/nwaku/issues/2171)) ([2b4ca4d0](https://github.com/waku-org/nwaku/commit/2b4ca4d0))
+- **networking:** lower dhigh to limit amplification factor ([#2168](https://github.com/waku-org/nwaku/issues/2168)) ([f0f69b32](https://github.com/waku-org/nwaku/commit/f0f69b32))
+- Minor Postgres optimizations ([#2166](https://github.com/waku-org/nwaku/issues/2166)) ([282c2e81](https://github.com/waku-org/nwaku/commit/282c2e81))
+- adding patch release instructions to release doc ([#2157](https://github.com/waku-org/nwaku/issues/2157)) ([cc01bb07](https://github.com/waku-org/nwaku/commit/cc01bb07))
+- **release:** update changelog for v0.21.1 release ([#2155](https://github.com/waku-org/nwaku/issues/2155)) ([b109a583](https://github.com/waku-org/nwaku/commit/b109a583))
+- adding ext-multiaddr-only CLI flag ([#2141](https://github.com/waku-org/nwaku/issues/2141)) ([944dfdaa](https://github.com/waku-org/nwaku/commit/944dfdaa))
+- bumping nim-libp2p to include WSS fix ([#2150](https://github.com/waku-org/nwaku/issues/2150)) ([817a7b2e](https://github.com/waku-org/nwaku/commit/817a7b2e))
+- **cbindings:** avoid using global var in libwaku.nim ([#2118](https://github.com/waku-org/nwaku/issues/2118)) ([1e8f5771](https://github.com/waku-org/nwaku/commit/1e8f5771))
+- adding postgres flag to manual docker job instructions ([#2139](https://github.com/waku-org/nwaku/issues/2139)) ([459331e3](https://github.com/waku-org/nwaku/commit/459331e3))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## Upgrade instructions
+
+* Note that the `--topic` CLI option is now deprecated in favor of a more specific options `--pubsub-topic` & `--content-topic`.
+
+## v0.21.3 (2023-11-09)
+
+This patch release adds the following feature:
+- Adding generateRlnKeystore subcommand for RLN membership generation
+
+### Features
+
+- rln-keystore-generator is now a subcommand ([#2189](https://github.com/waku-org/nwaku/issues/2189)) ([1e919177](https://github.com/waku-org/nwaku/commit/1e919177))
+
+This is a patch release that is fully backwards-compatible with release `v0.21.0`, `v0.21.1` and `v0.21.2`.
+
+It supports the same [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## v0.21.2 (2023-11-07)
+
+This patch release addresses the following issue:
+- Inability to send RLN messages through the REST API
+
+### Bug Fixes
+
+- **rest:** fix bug in rest api when sending rln message ([#2169](https://github.com/waku-org/nwaku/issues/2169)) ([33decd7a](https://github.com/waku-org/nwaku/commit/33decd7a))
+
+This is a patch release that is fully backwards-compatible with release `v0.21.0` and `v0.21.1`.
+
+It supports the same [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## v0.21.1 (2023-10-26)
+
+This patch release addresses the following issues:
+- WSS connections being suddenly terminated under rare conditions
+- Ability for the user to control announced multiaddresses
+
+### Changes
+
+- adding ext-multiaddr-only CLI flag ([#2141](https://github.com/waku-org/nwaku/issues/2141)) ([e2dfc2ed](https://github.com/waku-org/nwaku/commit/e2dfc2ed))
+- bumping nim-libp2p to include WSS fix ([#2150](https://github.com/waku-org/nwaku/issues/2150)) ([18b5149a](https://github.com/waku-org/nwaku/commit/18b5149a))
+
+This is a patch release that is fully backwards-compatible with release `v0.21.0`.
+
+It supports the same [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## v0.21.0 (2023-10-18)
+
+> Note: This is the last release supporting the `--topic` option. It is being deprecated in favor of a more specific options `--pubsub-topic` & `--content-topic`
+
+## What's Changed
+
+Release highlights:
+* Implemented a req/resp [protocol](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) that provides information about the node's medatadata
+* Added REST APIs for Filter v2 and Lightpush protocols' services
+* Ported /admin endpoint to REST
+* Added a size-based retention policy for the user to set a limit for SQLite storage used
+
+### Features
+
+- add new metadata protocol ([#2062](https://github.com/waku-org/nwaku/issues/2062)) ([d5c3ade5](https://github.com/waku-org/nwaku/commit/d5c3ade5))
+- /admin rest api endpoint ([#2094](https://github.com/waku-org/nwaku/issues/2094)) ([7b5c36b1](https://github.com/waku-org/nwaku/commit/7b5c36b1))
+- **coverage:** Add simple coverage ([#2067](https://github.com/waku-org/nwaku/issues/2067)) ([d864db3f](https://github.com/waku-org/nwaku/commit/d864db3f))
+- added RELAY openapi definitions ([#2081](https://github.com/waku-org/nwaku/issues/2081)) ([56dbe2a7](https://github.com/waku-org/nwaku/commit/56dbe2a7))
+- **wakucanary:** add latency measurement using ping protocol ([#2074](https://github.com/waku-org/nwaku/issues/2074)) ([6cb9a8da](https://github.com/waku-org/nwaku/commit/6cb9a8da))
+- Autosharding API for RELAY subscriptions ([#1983](https://github.com/waku-org/nwaku/issues/1983)) ([1763b1ef](https://github.com/waku-org/nwaku/commit/1763b1ef))
+- **networkmonitor:** add ping latencies, optimize reconnections ([#2068](https://github.com/waku-org/nwaku/issues/2068)) ([ed473545](https://github.com/waku-org/nwaku/commit/ed473545))
+- peer manager can filter peers by shard ([#2063](https://github.com/waku-org/nwaku/issues/2063)) ([0d9e9fbd](https://github.com/waku-org/nwaku/commit/0d9e9fbd))
+- lightpush rest api ([#2052](https://github.com/waku-org/nwaku/issues/2052)) ([02a814bd](https://github.com/waku-org/nwaku/commit/02a814bd))
+- HTTP REST API: Filter support v2 ([#1890](https://github.com/waku-org/nwaku/issues/1890)) ([dac072f8](https://github.com/waku-org/nwaku/commit/dac072f8))
+
+### Bug Fixes
+
+- fix wrong install of filter rest api ([#2133](https://github.com/waku-org/nwaku/issues/2133)) ([5277d122](https://github.com/waku-org/nwaku/commit/5277d122))
+- consider WS extMultiAddrs before publishing host address ([#2122](https://github.com/waku-org/nwaku/issues/2122)) ([a5b1cfd0](https://github.com/waku-org/nwaku/commit/a5b1cfd0))
+- return erring response if lightpush request is invalid ([#2083](https://github.com/waku-org/nwaku/issues/2083)) ([2c5eb427](https://github.com/waku-org/nwaku/commit/2c5eb427))
+- sqlite limited delete query bug ([#2111](https://github.com/waku-org/nwaku/issues/2111)) ([06bc433a](https://github.com/waku-org/nwaku/commit/06bc433a))
+- cluster id & sharding terminology ([#2104](https://github.com/waku-org/nwaku/issues/2104)) ([a47dc9e6](https://github.com/waku-org/nwaku/commit/a47dc9e6))
+- **ci:** update the dependency list in pre-release WF ([#2088](https://github.com/waku-org/nwaku/issues/2088)) ([e85f05b0](https://github.com/waku-org/nwaku/commit/e85f05b0))
+- **ci:** fix name of discord notify method ([aaf10e08](https://github.com/waku-org/nwaku/commit/aaf10e08))
+- update wakuv2 fleet DNS discovery enrtree ([89854a96](https://github.com/waku-org/nwaku/commit/89854a96))
+- libwaku.nim: unsubscribe -> unsubscribeAll to make it build properly ([#2082](https://github.com/waku-org/nwaku/issues/2082)) ([3264a4f5](https://github.com/waku-org/nwaku/commit/3264a4f5))
+- **archive:** dburl check ([#2071](https://github.com/waku-org/nwaku/issues/2071)) ([a27d005f](https://github.com/waku-org/nwaku/commit/a27d005f))
+- filter discv5 bootstrap nodes by shards ([#2073](https://github.com/waku-org/nwaku/issues/2073)) ([d178105d](https://github.com/waku-org/nwaku/commit/d178105d))
+- **rln-relay:** segfault when no params except rln-relay are passed in ([#2047](https://github.com/waku-org/nwaku/issues/2047)) ([45fe2d3b](https://github.com/waku-org/nwaku/commit/45fe2d3b))
+- **sqlite:** Properly set user_version to 7 so that the migration procedure is not started ([#2031](https://github.com/waku-org/nwaku/issues/2031)) ([aa3e1a66](https://github.com/waku-org/nwaku/commit/aa3e1a66))
+
+### Changes
+
+- remove js-node tests as release candidate dependencies ([#2123](https://github.com/waku-org/nwaku/issues/2123)) ([ce5fb340](https://github.com/waku-org/nwaku/commit/ce5fb340))
+- added size based retention policy ([#2098](https://github.com/waku-org/nwaku/issues/2098)) ([25d6e52e](https://github.com/waku-org/nwaku/commit/25d6e52e))
+- Clarify running instructions ([#2038](https://github.com/waku-org/nwaku/issues/2038)) ([12e8b122](https://github.com/waku-org/nwaku/commit/12e8b122))
+- **rln:** add more hardcoded memberhips to static group ([#2108](https://github.com/waku-org/nwaku/issues/2108)) ([1042cacd](https://github.com/waku-org/nwaku/commit/1042cacd))
+- Revert lightpush error handling to allow zero peer publish again succeed ([#2099](https://github.com/waku-org/nwaku/issues/2099)) ([f05528d4](https://github.com/waku-org/nwaku/commit/f05528d4))
+- adding NetConfig test suite ([#2091](https://github.com/waku-org/nwaku/issues/2091)) ([23b49ca5](https://github.com/waku-org/nwaku/commit/23b49ca5))
+- **cbindings:** Adding cpp example that integrates the 'libwaku' ([#2079](https://github.com/waku-org/nwaku/issues/2079)) ([8455b8dd](https://github.com/waku-org/nwaku/commit/8455b8dd))
+- **networkmonitor:** refactor setConnectedPeersMetrics, make it partially concurrent, add version ([#2080](https://github.com/waku-org/nwaku/issues/2080)) ([c5aa9704](https://github.com/waku-org/nwaku/commit/c5aa9704))
+- resolving DNS IP and publishing it when no extIp is provided ([#2030](https://github.com/waku-org/nwaku/issues/2030)) ([7797b2cd](https://github.com/waku-org/nwaku/commit/7797b2cd))
+- Adding -d:postgres flag when creating a Docker image for release and PRs ([#2076](https://github.com/waku-org/nwaku/issues/2076)) ([7a376f59](https://github.com/waku-org/nwaku/commit/7a376f59))
+- Moved external APIs out of node ([#2069](https://github.com/waku-org/nwaku/issues/2069)) ([3e72e830](https://github.com/waku-org/nwaku/commit/3e72e830))
+- bump nim-libp2p, nim-toml-serialization, nim-unicodedb, nim-unittest2, nim-websock, nim-zlib, & nimbus-build-system ([#2065](https://github.com/waku-org/nwaku/issues/2065)) ([dc25057a](https://github.com/waku-org/nwaku/commit/dc25057a))
+- **ci:** add js-waku as a dependency for pre-release createion ([#2022](https://github.com/waku-org/nwaku/issues/2022)) ([28b04000](https://github.com/waku-org/nwaku/commit/28b04000))
+- Updating nim-chronicles, nim-chronos, nim-presto, nimcrypto, nim-libp2p, and nim-nat-transversal ([#2043](https://github.com/waku-org/nwaku/issues/2043)) ([f617cd97](https://github.com/waku-org/nwaku/commit/f617cd97))
+- **cbindings:** Thread-safe communication between the main thread and the Waku Thread ([#1978](https://github.com/waku-org/nwaku/issues/1978)) ([72f90663](https://github.com/waku-org/nwaku/commit/72f90663))
+- **rln-relay:** logs, updated submodule, leaves_set metric ([#2024](https://github.com/waku-org/nwaku/issues/2024)) ([2e515a06](https://github.com/waku-org/nwaku/commit/2e515a06))
+- **release:** update changelog for v0.20.0 release ([#2026](https://github.com/waku-org/nwaku/issues/2026)) ([9085b1b3](https://github.com/waku-org/nwaku/commit/9085b1b3))
+- **postgres:** not loading the libpq library by default & better user feedback ([#2028](https://github.com/waku-org/nwaku/issues/2028)) ([e8602021](https://github.com/waku-org/nwaku/commit/e8602021))
+- move SubscriptionManager under waku_core ([#2025](https://github.com/waku-org/nwaku/issues/2025)) ([563b2b20](https://github.com/waku-org/nwaku/commit/563b2b20))
+- **README:** List possible WSL Issue ([#1995](https://github.com/waku-org/nwaku/issues/1995)) ([ebe715e9](https://github.com/waku-org/nwaku/commit/ebe715e9))
+- **ci:** add js-waku test to pre-release workflow ([#2017](https://github.com/waku-org/nwaku/issues/2017)) ([e8776fd6](https://github.com/waku-org/nwaku/commit/e8776fd6))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## Upgrade instructions
+
+* Note that the `--topic` CLI option is being deprecated in favor of a more specific options `--pubsub-topic` & `--content-topic`. This is the last release supporting the `--topic` option.
+* The size-based retention policy has been tested with SQLite storage and is still on validation phases for Postgres
+
+## 2023-09-14 v0.20.0
+
+> Note: IP address 0.0.0.0 is no longer advertised by a node
+
+> Note: Multiple CLI options have been removed in this release, please see _Upgrade instructions_ section for details.
+
+## What's Changed
+
+Release highlights:
+* RLN is now part of standard release (is no longer EXPERIMENTAL feature)
+* Interop tests between nwaku and js-waku are now gating PRs and releases
+* Libwaku has been made more threadsafe (1 out of 3 improvements applied.)
+* Added autosharding option on various protocol APIs
+
+
+
+### Features
+
+- **rln-relay:** removed rln from experimental 🚀 ([#2001](https://github.com/waku-org/nwaku/issues/2001)) ([645b0343](https://github.com/waku-org/nwaku/commit/645b0343))
+- Rest endoint /health for rln ([#2011](https://github.com/waku-org/nwaku/issues/2011)) ([fc6194bb](https://github.com/waku-org/nwaku/commit/fc6194bb))
+- **rln_db_inspector:** create rln_db_inspector tool ([#1999](https://github.com/waku-org/nwaku/issues/1999)) ([ec42e2c7](https://github.com/waku-org/nwaku/commit/ec42e2c7))
+- **relay:** ordered validator execution ([#1966](https://github.com/waku-org/nwaku/issues/1966)) ([debc5f19](https://github.com/waku-org/nwaku/commit/debc5f19))
+- **discv5:** topic subscriptions update discv5 filter predicate ([#1918](https://github.com/waku-org/nwaku/issues/1918)) ([4539dfc7](https://github.com/waku-org/nwaku/commit/4539dfc7))
+- topic subscriptions updates discv5 ENR ([#1875](https://github.com/waku-org/nwaku/issues/1875)) ([c369b329](https://github.com/waku-org/nwaku/commit/c369b329))
+- **rln_keystore_generator:** wired to onchain group manager ([#1931](https://github.com/waku-org/nwaku/issues/1931)) ([c9b48ea1](https://github.com/waku-org/nwaku/commit/c9b48ea1))
+- **rln:** init rln_keystore_generator ([#1925](https://github.com/waku-org/nwaku/issues/1925)) ([3d849541](https://github.com/waku-org/nwaku/commit/3d849541))
+- update various protocols to autoshard ([#1857](https://github.com/waku-org/nwaku/issues/1857)) ([cf301396](https://github.com/waku-org/nwaku/commit/cf301396))
+
+### Bug Fixes
+
+- **rln-relay:** waku_rln_number_registered_memberships metrics appropriately handled ([#2018](https://github.com/waku-org/nwaku/issues/2018)) ([a4e78330](https://github.com/waku-org/nwaku/commit/a4e78330))
+- prevent IP 0.0.0.0 from being published and update peers with empty ENR data ([#1982](https://github.com/waku-org/nwaku/issues/1982)) ([47ae19c1](https://github.com/waku-org/nwaku/commit/47ae19c1))
+- **rln-relay:** missed roots during sync ([#2015](https://github.com/waku-org/nwaku/issues/2015)) ([21604e6b](https://github.com/waku-org/nwaku/commit/21604e6b))
+- **p2p:** fix possible connectivity issue ([#1996](https://github.com/waku-org/nwaku/issues/1996)) ([7d9d8a3f](https://github.com/waku-org/nwaku/commit/7d9d8a3f))
+- **rln-db-inspector:** use valueOr pattern ([#2012](https://github.com/waku-org/nwaku/issues/2012)) ([a8095d87](https://github.com/waku-org/nwaku/commit/a8095d87))
+- **tests:** relay tests use random port to avoid conflict ([#1998](https://github.com/waku-org/nwaku/issues/1998)) ([b991682b](https://github.com/waku-org/nwaku/commit/b991682b))
+- **ci:** incorrect use of braces ([#1987](https://github.com/waku-org/nwaku/issues/1987)) ([4ed41457](https://github.com/waku-org/nwaku/commit/4ed41457))
+- **Makefile:** invalid path to crate build ([#1981](https://github.com/waku-org/nwaku/issues/1981)) ([1a318c29](https://github.com/waku-org/nwaku/commit/1a318c29))
+- --topic should be ignore when using --pubsub-topic or --content-topic ([#1977](https://github.com/waku-org/nwaku/issues/1977)) ([037b1662](https://github.com/waku-org/nwaku/commit/037b1662))
+- **tests:** fix flaky test ([#1972](https://github.com/waku-org/nwaku/issues/1972)) ([f262397d](https://github.com/waku-org/nwaku/commit/f262397d))
+- **rln-relay:** deserialization of valid merkle roots ([#1973](https://github.com/waku-org/nwaku/issues/1973)) ([d262837e](https://github.com/waku-org/nwaku/commit/d262837e))
+- **ci:** rename tools artifact to prevent conflict ([#1971](https://github.com/waku-org/nwaku/issues/1971)) ([26c06b27](https://github.com/waku-org/nwaku/commit/26c06b27))
+- **Makefile:** rln was enabled by default ([#1964](https://github.com/waku-org/nwaku/issues/1964)) ([9b1d2904](https://github.com/waku-org/nwaku/commit/9b1d2904))
+- **rln-relay:** modify keystore credentials logic ([#1956](https://github.com/waku-org/nwaku/issues/1956)) ([e7b2b88f](https://github.com/waku-org/nwaku/commit/e7b2b88f))
+- **Makefile:** error out if rln-keystore-generator not compiled with rln flag ([#1960](https://github.com/waku-org/nwaku/issues/1960)) ([ac258550](https://github.com/waku-org/nwaku/commit/ac258550))
+- **rln-relay:** sync from deployed block number ([#1955](https://github.com/waku-org/nwaku/issues/1955)) ([bd3be219](https://github.com/waku-org/nwaku/commit/bd3be219))
+- **rln-relay:** window of acceptable roots synced to rln metadata ([#1953](https://github.com/waku-org/nwaku/issues/1953)) ([01634f57](https://github.com/waku-org/nwaku/commit/01634f57))
+- **rln-relay:** bump zerokit to v0.3.2 ([#1951](https://github.com/waku-org/nwaku/issues/1951)) ([32aa1c5b](https://github.com/waku-org/nwaku/commit/32aa1c5b))
+- **rln-relay:** flush_interval incorrectly set ([#1933](https://github.com/waku-org/nwaku/issues/1933)) ([c07d63db](https://github.com/waku-org/nwaku/commit/c07d63db))
+- **rln-relay:** RLN DB should be aware of chain and contract address ([#1932](https://github.com/waku-org/nwaku/issues/1932)) ([1ae5b5a9](https://github.com/waku-org/nwaku/commit/1ae5b5a9))
+- **rln-relay:** waitFor startup, otherwise valid proofs will be marked invalid ([#1920](https://github.com/waku-org/nwaku/issues/1920)) ([6c6302f9](https://github.com/waku-org/nwaku/commit/6c6302f9))
+- **test:** fix flaky rln test ([#1923](https://github.com/waku-org/nwaku/issues/1923)) ([0ac8a7f0](https://github.com/waku-org/nwaku/commit/0ac8a7f0))
+- **rln-relay:** remove registration capability ([#1916](https://github.com/waku-org/nwaku/issues/1916)) ([f08315cd](https://github.com/waku-org/nwaku/commit/f08315cd))
+- **rln-relay:** invalid start index being set results in invalid proofs ([#1915](https://github.com/waku-org/nwaku/issues/1915)) ([b3bb7a11](https://github.com/waku-org/nwaku/commit/b3bb7a11))
+- **rln-relay:** should error out on rln-relay mount failure ([#1904](https://github.com/waku-org/nwaku/issues/1904)) ([8c568cab](https://github.com/waku-org/nwaku/commit/8c568cab))
+- **rln-relay:** timeout on macos runners, use fixed version of ganache ([#1913](https://github.com/waku-org/nwaku/issues/1913)) ([c9772af0](https://github.com/waku-org/nwaku/commit/c9772af0))
+- no enr record in chat2 ([#1907](https://github.com/waku-org/nwaku/issues/1907)) ([fc604ca5](https://github.com/waku-org/nwaku/commit/fc604ca5))
+
+### Changes
+
+- **ci:** add js-waku test to pre-release workflow ([#2017](https://github.com/waku-org/nwaku/issues/2017)) ([e8776fd6](https://github.com/waku-org/nwaku/commit/e8776fd6))
+- **rln-relay:** updated docs ([#1993](https://github.com/waku-org/nwaku/issues/1993)) ([76e34077](https://github.com/waku-org/nwaku/commit/76e34077))
+- **ci:** execute js-waku integration tests on image build ([#2006](https://github.com/waku-org/nwaku/issues/2006)) ([5d976df9](https://github.com/waku-org/nwaku/commit/5d976df9))
+- **rln-relay:** add isReady check ([#1989](https://github.com/waku-org/nwaku/issues/1989)) ([5638bd06](https://github.com/waku-org/nwaku/commit/5638bd06))
+- **rln-relay:** clean up nullifier table every MaxEpochGap ([#1994](https://github.com/waku-org/nwaku/issues/1994)) ([483f40c8](https://github.com/waku-org/nwaku/commit/483f40c8))
+- **ci:** use commit instead of master for docker image ([#1990](https://github.com/waku-org/nwaku/issues/1990)) ([98850192](https://github.com/waku-org/nwaku/commit/98850192))
+- **rln-relay:** log levels for certain logs ([#1986](https://github.com/waku-org/nwaku/issues/1986)) ([97a7c9d0](https://github.com/waku-org/nwaku/commit/97a7c9d0))
+- **rln-relay:** use the only key from keystore if only 1 exists ([#1984](https://github.com/waku-org/nwaku/issues/1984)) ([a14c3261](https://github.com/waku-org/nwaku/commit/a14c3261))
+- **ci:** enable experimental for the PR image builds ([#1976](https://github.com/waku-org/nwaku/issues/1976)) ([1b835b4e](https://github.com/waku-org/nwaku/commit/1b835b4e))
+- **rln-relay:** confirm that the provided credential is correct using onchain query ([#1980](https://github.com/waku-org/nwaku/issues/1980)) ([be48891f](https://github.com/waku-org/nwaku/commit/be48891f))
+- **api:** validate rln message before sending (rest + rpc) ([#1968](https://github.com/waku-org/nwaku/issues/1968)) ([05c98864](https://github.com/waku-org/nwaku/commit/05c98864))
+- **cbindings:** Thread-safe libwaku. WakuNode instance created directly from the Waku Thread ([#1957](https://github.com/waku-org/nwaku/issues/1957)) ([68e8d9a7](https://github.com/waku-org/nwaku/commit/68e8d9a7))
+- add debug log indicating succesful message pushes and also log the message hash ([#1965](https://github.com/waku-org/nwaku/issues/1965)) ([e272bec9](https://github.com/waku-org/nwaku/commit/e272bec9))
+- **rln-keystore-generator:** log out the membership index upon registration ([#1963](https://github.com/waku-org/nwaku/issues/1963)) ([7d53aec1](https://github.com/waku-org/nwaku/commit/7d53aec1))
+- **rln-relay:** integrate waku rln registry ([#1943](https://github.com/waku-org/nwaku/issues/1943)) ([cc9f8d42](https://github.com/waku-org/nwaku/commit/cc9f8d42))
+- **ci:** add a job checking config options and db schema ([#1927](https://github.com/waku-org/nwaku/issues/1927)) ([505d1967](https://github.com/waku-org/nwaku/commit/505d1967))
+- **rln_keystore_generator:** generate and persist credentials ([#1928](https://github.com/waku-org/nwaku/issues/1928)) ([07945a37](https://github.com/waku-org/nwaku/commit/07945a37))
+- **rln-relay:** rename keystore application to waku-rln-relay ([#1924](https://github.com/waku-org/nwaku/issues/1924)) ([8239b455](https://github.com/waku-org/nwaku/commit/8239b455))
+- **rln:** remove old and add new rln metric ([#1926](https://github.com/waku-org/nwaku/issues/1926)) ([56c228f8](https://github.com/waku-org/nwaku/commit/56c228f8))
+- **rln:** run rln in all relay pubsubtopics + remove cli flags ([#1917](https://github.com/waku-org/nwaku/issues/1917)) ([af95b571](https://github.com/waku-org/nwaku/commit/af95b571))
+- **release:** update changelog for delayed v0.19.0 release ([#1911](https://github.com/waku-org/nwaku/issues/1911)) ([78690787](https://github.com/waku-org/nwaku/commit/78690787))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## Upgrade instructions
+
+* Note that the `--topic` CLI option is being deprecated in favor of a more specific options `--pubsub-topic` & `--content-topic`. The `--topic` option will be available for next release with a deprecation note.
+* CLI option `--store-resume-peer` has been removed.
+* Following options related to RLN have been removed:
+ * `--rln-relay-membership-group-index`
+ * `--rln-relay-pubsub-topic`
+ * `--rln-relay-content-topic`
+
+
+## 2023-08-16 v0.19.0
+
+> Note that the `--topic` CLI option is being deprecated in favor a more specific option `--pubsub-topic`.
+
+> The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+
+## What's Changed
+
+Release highlights:
+* Improved connection management, including management for non-relay peers and limiting the number of connections from a single IP
+* Postgres support has been added as a backend for archive module
+* RLN initialization optimizations
+* Update to the latest nim-libp2p
+* Removed Waku v1 and also references to `v2` from the current version
+* Basic implementation of Autosharding for the Waku Network
+* REST API implementation for Filter protocol
+
+### Features
+
+- **ci:** add docker image builds per PR ([#1881](https://github.com/waku-org/nwaku/issues/1881)) ([84f94d5d](https://github.com/waku-org/nwaku/commit/84f94d5d))
+- Rest API interface for legacy (v1) filter service. ([#1851](https://github.com/waku-org/nwaku/issues/1851)) ([08ff6672](https://github.com/waku-org/nwaku/commit/08ff6672))
+- autosharding content topics in config ([#1856](https://github.com/waku-org/nwaku/issues/1856)) ([afb93e29](https://github.com/waku-org/nwaku/commit/afb93e29))
+- autosharding core algorithm ([#1854](https://github.com/waku-org/nwaku/issues/1854)) ([bbff1ac1](https://github.com/waku-org/nwaku/commit/bbff1ac1))
+- **cbindings:** tiny waku relay example in Python ([#1793](https://github.com/waku-org/nwaku/issues/1793)) ([0b2cfae5](https://github.com/waku-org/nwaku/commit/0b2cfae5))
+- **rln-relay:** close db connection appropriately ([#1858](https://github.com/waku-org/nwaku/issues/1858)) ([76c73b62](https://github.com/waku-org/nwaku/commit/76c73b62))
+- enable TcpNoDelay ([#1470](https://github.com/waku-org/nwaku/issues/1470)) ([08f3bba3](https://github.com/waku-org/nwaku/commit/08f3bba3))
+- limit relay connections below max conns ([#1813](https://github.com/waku-org/nwaku/issues/1813)) ([17b24cde](https://github.com/waku-org/nwaku/commit/17b24cde))
+- **postgres:** integration of postgres in wakunode2 ([#1808](https://github.com/waku-org/nwaku/issues/1808)) ([88b7481f](https://github.com/waku-org/nwaku/commit/88b7481f))
+- discovery peer filtering for relay shard ([#1804](https://github.com/waku-org/nwaku/issues/1804)) ([a4da87bb](https://github.com/waku-org/nwaku/commit/a4da87bb))
+- **rln-relay:** resume onchain sync from persisted tree db ([#1805](https://github.com/waku-org/nwaku/issues/1805)) ([bbded9ee](https://github.com/waku-org/nwaku/commit/bbded9ee))
+- **rln-relay:** metadata ffi api ([#1803](https://github.com/waku-org/nwaku/issues/1803)) ([045f07c6](https://github.com/waku-org/nwaku/commit/045f07c6))
+
+### Bug Fixes
+
+- bring back default topic in config ([#1902](https://github.com/waku-org/nwaku/issues/1902)) ([d5d2243c](https://github.com/waku-org/nwaku/commit/d5d2243c))
+- **ci:** only add comment on PR and do not duplicate it ([#1908](https://github.com/waku-org/nwaku/issues/1908)) ([b785b6ba](https://github.com/waku-org/nwaku/commit/b785b6ba))
+- **ci:** add mising OS arch option to image build ([#1905](https://github.com/waku-org/nwaku/issues/1905)) ([2575f3c4](https://github.com/waku-org/nwaku/commit/2575f3c4))
+- **wakucanary:** add missing return on timeout ([#1901](https://github.com/waku-org/nwaku/issues/1901)) ([7dce0b9e](https://github.com/waku-org/nwaku/commit/7dce0b9e))
+- fixes out of bounds crash when waku2 is not set ([#1895](https://github.com/waku-org/nwaku/issues/1895)) ([03363f1b](https://github.com/waku-org/nwaku/commit/03363f1b))
+- **wakucanary:** add enr record to builder ([#1882](https://github.com/waku-org/nwaku/issues/1882)) ([831a093f](https://github.com/waku-org/nwaku/commit/831a093f))
+- check nil before calling clearTimer ([#1869](https://github.com/waku-org/nwaku/issues/1869)) ([2fc48842](https://github.com/waku-org/nwaku/commit/2fc48842))
+- **rln-relay:** mark duplicated messages as spam ([#1867](https://github.com/waku-org/nwaku/issues/1867)) ([4756ccc1](https://github.com/waku-org/nwaku/commit/4756ccc1))
+- **ci:** do not depend on number of procesors with job name ([#1863](https://github.com/waku-org/nwaku/issues/1863)) ([c560af11](https://github.com/waku-org/nwaku/commit/c560af11))
+- **libp2p:** Updating nim-libp2p to fix the `wss` connectivity issue ([#1848](https://github.com/waku-org/nwaku/issues/1848)) ([1d3410c7](https://github.com/waku-org/nwaku/commit/1d3410c7))
+- **rln-relay:** chunk event fetching ([#1830](https://github.com/waku-org/nwaku/issues/1830)) ([e4d9ee1f](https://github.com/waku-org/nwaku/commit/e4d9ee1f))
+- **discv5:** Fixing issue that prevented the wakunode2 from starting ([#1829](https://github.com/waku-org/nwaku/issues/1829)) ([3aefade6](https://github.com/waku-org/nwaku/commit/3aefade6))
+- sanity-check the docker image start ([ae05f0a8](https://github.com/waku-org/nwaku/commit/ae05f0a8))
+- **ci:** fix broken test with wrong import ([#1820](https://github.com/waku-org/nwaku/issues/1820)) ([4573e8c5](https://github.com/waku-org/nwaku/commit/4573e8c5))
+- temporary fix to disable default experimental builds on fleets ([#1810](https://github.com/waku-org/nwaku/issues/1810)) ([e9028618](https://github.com/waku-org/nwaku/commit/e9028618))
+- **rln-relay:** tree race condition upon initialization ([#1807](https://github.com/waku-org/nwaku/issues/1807)) ([f8e270fb](https://github.com/waku-org/nwaku/commit/f8e270fb))
+- fix mac docker build alpine version ([#1801](https://github.com/waku-org/nwaku/issues/1801)) ([fce845bb](https://github.com/waku-org/nwaku/commit/fce845bb))
+- **rln-relay:** flaky static group manager test ([#1798](https://github.com/waku-org/nwaku/issues/1798)) ([0e9ecbd6](https://github.com/waku-org/nwaku/commit/0e9ecbd6))
+
+### Changes
+
+- remove references to v2 ([#1898](https://github.com/waku-org/nwaku/issues/1898)) ([b9d5d28a](https://github.com/waku-org/nwaku/commit/b9d5d28a))
+- **submodules:** use zerokit v0.3.1 only ([#1886](https://github.com/waku-org/nwaku/issues/1886)) ([311f5ea0](https://github.com/waku-org/nwaku/commit/311f5ea0))
+- remove Waku v1 and wakubridge code ([#1874](https://github.com/waku-org/nwaku/issues/1874)) ([ab344a9d](https://github.com/waku-org/nwaku/commit/ab344a9d))
+- **cbindings:** libwaku - run waku node in a secondary working thread ([#1865](https://github.com/waku-org/nwaku/issues/1865)) ([069c1ad2](https://github.com/waku-org/nwaku/commit/069c1ad2))
+- update docs link ([#1850](https://github.com/waku-org/nwaku/issues/1850)) ([d2b6075b](https://github.com/waku-org/nwaku/commit/d2b6075b))
+- **changelog:** release notes for v0.19.0 ([#1861](https://github.com/waku-org/nwaku/issues/1861)) ([32c1276f](https://github.com/waku-org/nwaku/commit/32c1276f))
+- **rln-relay:** verify proofs based on bandwidth usage ([#1844](https://github.com/waku-org/nwaku/issues/1844)) ([3fe4522a](https://github.com/waku-org/nwaku/commit/3fe4522a))
+- **rln-relay:** bump zerokit ([#1838](https://github.com/waku-org/nwaku/issues/1838)) ([4f0bdf9a](https://github.com/waku-org/nwaku/commit/4f0bdf9a))
+- bump nim-libp2p to 224f92e ([661638da](https://github.com/waku-org/nwaku/commit/661638da))
+- **refactor:** Move record creation & fix libwaku compilation ([#1833](https://github.com/waku-org/nwaku/issues/1833)) ([97d3b9f7](https://github.com/waku-org/nwaku/commit/97d3b9f7))
+- discv5 re-org clean-up ([#1823](https://github.com/waku-org/nwaku/issues/1823)) ([cf46fb7c](https://github.com/waku-org/nwaku/commit/cf46fb7c))
+- **networking:** disconnect due to colocation ip in conn handler ([#1821](https://github.com/waku-org/nwaku/issues/1821)) ([e12c979c](https://github.com/waku-org/nwaku/commit/e12c979c))
+- **rln-relay:** bump zerokit for version fix ([#1822](https://github.com/waku-org/nwaku/issues/1822)) ([add294a9](https://github.com/waku-org/nwaku/commit/add294a9))
+- move discv5 out of node. ([#1818](https://github.com/waku-org/nwaku/issues/1818)) ([62d36530](https://github.com/waku-org/nwaku/commit/62d36530))
+- **archive:** Moving waku archive logic from app.nim to the archive module ([#1817](https://github.com/waku-org/nwaku/issues/1817)) ([52894a82](https://github.com/waku-org/nwaku/commit/52894a82))
+- add peer manager config to builder ([#1816](https://github.com/waku-org/nwaku/issues/1816)) ([71c4ac16](https://github.com/waku-org/nwaku/commit/71c4ac16))
+- discv5 re-org setup ([#1815](https://github.com/waku-org/nwaku/issues/1815)) ([44f9d8dc](https://github.com/waku-org/nwaku/commit/44f9d8dc))
+- **databases:** Creation of the databases folder to keep the logic for sqlite and postgres ([#1811](https://github.com/waku-org/nwaku/issues/1811)) ([a44d4bfb](https://github.com/waku-org/nwaku/commit/a44d4bfb))
+- **deps:** bump libp2p & websock ([#1800](https://github.com/waku-org/nwaku/issues/1800)) ([f6e89c31](https://github.com/waku-org/nwaku/commit/f6e89c31))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation has been removed from this repository and can be found in a separate [Waku Legacy](https://github.com/waku-org/waku-legacy) repository.
+
+## Upgrade instructions
+
+* Note that the `--topic` CLI option is being deprecated in favor a more specific option `--pubsub-topic`. The `--topic` option will be available for next 2 releases with a deprecation note.
+
+## 2023-06-14 v0.18.0
+
+> Note that there is a new naming scheme for release artifacts.
+
+## What's Changed
+
+Release highlights:
+* Support for Gossipsub scoring
+* [Rendezvous discovery protocol](https://docs.libp2p.io/concepts/discovery-routing/rendezvous/) enabled by default with relay
+* Initial support for postgresql as Store backend
+* Atomic operations for insertions and deletions included in rln-relay
+
+### Features
+
+- **postgres:** complete implementation of driver and apply more tests ([#1785](https://github.com/waku-org/nwaku/issues/1785)) ([5fc5770d](https://github.com/waku-org/nwaku/commit/5fc5770d))
+- **postgres:** adding a postgres async pool to make the db interactions asynchronous ([#1779](https://github.com/waku-org/nwaku/issues/1779)) ([cb2e3d86](https://github.com/waku-org/nwaku/commit/cb2e3d86))
+- **rln-relay:** pass in index to keystore credentials ([#1777](https://github.com/waku-org/nwaku/issues/1777)) ([a00aa8cc](https://github.com/waku-org/nwaku/commit/a00aa8cc))
+- **networking:** integrate gossipsub scoring ([#1769](https://github.com/waku-org/nwaku/issues/1769)) ([34a92631](https://github.com/waku-org/nwaku/commit/34a92631))
+- **discv5:** added find random nodes with predicate ([#1762](https://github.com/waku-org/nwaku/issues/1762)) ([#1763](https://github.com/waku-org/nwaku/issues/1763)) ([21737c7c](https://github.com/waku-org/nwaku/commit/21737c7c))
+- **wakunode2:** enable libp2p rendezvous protocol by default ([#1770](https://github.com/waku-org/nwaku/issues/1770)) ([835a409d](https://github.com/waku-org/nwaku/commit/835a409d))
+- **postgresql:** align previous work's PR[#1590](https://github.com/waku-org/nwaku/issues/1590) changes into master ([#1764](https://github.com/waku-org/nwaku/issues/1764)) ([7df6f4c8](https://github.com/waku-org/nwaku/commit/7df6f4c8))
+- **networking:** prune peers from same ip beyond collocation limit ([#1765](https://github.com/waku-org/nwaku/issues/1765)) ([047d1cf0](https://github.com/waku-org/nwaku/commit/047d1cf0))
+- **ci:** add nightly builds ([#1758](https://github.com/waku-org/nwaku/issues/1758)) ([473af70a](https://github.com/waku-org/nwaku/commit/473af70a))
+- **postgresql:** 1st commit to async sql (waku_archive/driver...) ([#1755](https://github.com/waku-org/nwaku/issues/1755)) ([59ca03a8](https://github.com/waku-org/nwaku/commit/59ca03a8))
+- **ci:** add release-notes target ([#1734](https://github.com/waku-org/nwaku/issues/1734)) ([ceb54b18](https://github.com/waku-org/nwaku/commit/ceb54b18))
+- **rln-relay:** use new atomic_operation ffi api ([#1733](https://github.com/waku-org/nwaku/issues/1733)) ([611e9539](https://github.com/waku-org/nwaku/commit/611e9539))
+
+### Bug Fixes
+
+- **ci:** enforce basic CPU instruction set to prevent CI issues ([#1759](https://github.com/waku-org/nwaku/issues/1759)) ([35520bd0](https://github.com/waku-org/nwaku/commit/35520bd0))
+- **test:** wait more for gossip ([#1753](https://github.com/waku-org/nwaku/issues/1753)) ([0fce3d83](https://github.com/waku-org/nwaku/commit/0fce3d83))
+- **rln-relay:** keystore usage ([#1750](https://github.com/waku-org/nwaku/issues/1750)) ([36266b43](https://github.com/waku-org/nwaku/commit/36266b43))
+- **ci:** fix flaky test for dos topic ([#1747](https://github.com/waku-org/nwaku/issues/1747)) ([46e231d0](https://github.com/waku-org/nwaku/commit/46e231d0))
+- **rln-relay:** trace log ([#1743](https://github.com/waku-org/nwaku/issues/1743)) ([5eae60e8](https://github.com/waku-org/nwaku/commit/5eae60e8))
+- **ci:** make experimental default to true in fleet deployment ([#1742](https://github.com/waku-org/nwaku/issues/1742)) ([b148c305](https://github.com/waku-org/nwaku/commit/b148c305))
+
+### Changes
+
+- **rln:** bump zerokit ([#1787](https://github.com/waku-org/nwaku/issues/1787)) ([9c04b59b](https://github.com/waku-org/nwaku/commit/9c04b59b))
+- **ci:** extend and rename nightly workflow to support RC builds ([#1784](https://github.com/waku-org/nwaku/issues/1784)) ([96074071](https://github.com/waku-org/nwaku/commit/96074071))
+- **rln-relay:** pass in the path to the tree db ([#1782](https://github.com/waku-org/nwaku/issues/1782)) ([dba84248](https://github.com/waku-org/nwaku/commit/dba84248))
+- **rln-relay:** update tree_config ([#1781](https://github.com/waku-org/nwaku/issues/1781)) ([ba8ec704](https://github.com/waku-org/nwaku/commit/ba8ec704))
+- **ci:** properly set os and architecture for nightly and release ([#1780](https://github.com/waku-org/nwaku/issues/1780)) ([44bcf0f2](https://github.com/waku-org/nwaku/commit/44bcf0f2))
+- **ci:** remove add-to-project workflow ([#1778](https://github.com/waku-org/nwaku/issues/1778)) ([a9505892](https://github.com/waku-org/nwaku/commit/a9505892))
+- **ci:** add experimental builds to nightly ([#1761](https://github.com/waku-org/nwaku/issues/1761)) ([ffac7761](https://github.com/waku-org/nwaku/commit/ffac7761))
+- **px:** close px streams after resp is sent ([#1746](https://github.com/waku-org/nwaku/issues/1746)) ([3c2d2891](https://github.com/waku-org/nwaku/commit/3c2d2891))
+- **docs:** fix docs and mark some as deprecated ([#1754](https://github.com/waku-org/nwaku/issues/1754)) ([b51fb616](https://github.com/waku-org/nwaku/commit/b51fb616))
+- **makefile:** unify where chronicles_log_level is set ([#1748](https://github.com/waku-org/nwaku/issues/1748)) ([39902dc2](https://github.com/waku-org/nwaku/commit/39902dc2))
+- **rln-relay:** docs and config update for testnet 3 ([#1738](https://github.com/waku-org/nwaku/issues/1738)) ([bb9d231b](https://github.com/waku-org/nwaku/commit/bb9d231b))
+- **rln-relay:** update metrics dashboard ([#1745](https://github.com/waku-org/nwaku/issues/1745)) ([0ced2195](https://github.com/waku-org/nwaku/commit/0ced2195))
+- **rln-relay:** updated metrics for testnet 3 ([#1744](https://github.com/waku-org/nwaku/issues/1744)) ([62578746](https://github.com/waku-org/nwaku/commit/62578746))
+- **networking:** set and use target outbound connections + prune ([#1739](https://github.com/waku-org/nwaku/issues/1739)) ([87f694a8](https://github.com/waku-org/nwaku/commit/87f694a8))
+- proper use of setupNat ([#1740](https://github.com/waku-org/nwaku/issues/1740)) ([665484c1](https://github.com/waku-org/nwaku/commit/665484c1))
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## Upgrade instructions
+
+There is a new naming scheme for release artifacts - `nwaku-${ARCHITECTURE}-${OS}-${VERSION}.tar.gz`. If you use any automation to download latest release, you may need to update it.
+
+The `--topics` config option has been deprecated to unify the configuration style. It is still available in this release but will be removed in the next one. The new option `--topic` is introduced, which can be used repeatedly to achieve the same behavior.
+
+## 2023-05-17 v0.17.0
+
+> Note that the --topics config item has been deprecated and support will be dropped in future releases. To configure support for multiple pubsub topics, use the new --topic parameter repeatedly.
+
+## What's Changed
+
+Release highlights:
+* New REST API for Waku Store protocol.
+* New Filter protocol implentation. See [12/WAKU2-FILTER](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md).
+* Initial C bindings support.
+* Support for Heaptrack to investigate memory utilization ([tutorial](https://github.com/waku-org/nwaku/blob/master/docs/tutorial/heaptrack.md)).
+
+### Features
+
+- **cbindings:** first commit - waku relay ([#1632](https://github.com/waku-org/nwaku/issues/1632)) ([#1714](https://github.com/waku-org/nwaku/issues/1714)) ([2defbd23](https://github.com/waku-org/nwaku/commit/2defbd23))
+- example using filter and lightpush ([#1720](https://github.com/waku-org/nwaku/issues/1720)) ([8987d4a3](https://github.com/waku-org/nwaku/commit/8987d4a3))
+- configure protected topics via cli ([#1696](https://github.com/waku-org/nwaku/issues/1696)) ([16b44523](https://github.com/waku-org/nwaku/commit/16b44523))
+- **mem-analysis:** Adding Dockerfile_with_heaptrack ([#1681](https://github.com/waku-org/nwaku/issues/1681)) ([9b9172ab](https://github.com/waku-org/nwaku/commit/9b9172ab))
+- add metrics with msg size histogram ([#1697](https://github.com/waku-org/nwaku/issues/1697)) ([67e96ba8](https://github.com/waku-org/nwaku/commit/67e96ba8))
+- curate peers shared over px protocol ([#1671](https://github.com/waku-org/nwaku/issues/1671)) ([14305c61](https://github.com/waku-org/nwaku/commit/14305c61))
+- **enr:** added support for relay shards field ([96162536](https://github.com/waku-org/nwaku/commit/96162536))
+- add tools maket target and build tools in CI ([#1668](https://github.com/waku-org/nwaku/issues/1668)) ([d5979e94](https://github.com/waku-org/nwaku/commit/d5979e94))
+- integrate new filter protocol, other improvements ([#1637](https://github.com/waku-org/nwaku/issues/1637)) ([418efca2](https://github.com/waku-org/nwaku/commit/418efca2))
+- **rest-api-store:** new rest api to retrieve store waku messages ([#1611](https://github.com/waku-org/nwaku/issues/1611)) ([#1630](https://github.com/waku-org/nwaku/issues/1630)) ([b2acb54d](https://github.com/waku-org/nwaku/commit/b2acb54d))
+- **node:** added waku node builder type ([e931fa5d](https://github.com/waku-org/nwaku/commit/e931fa5d))
+- dos protected topic relay msgs based on meta field ([#1614](https://github.com/waku-org/nwaku/issues/1614)) ([c26dcb2b](https://github.com/waku-org/nwaku/commit/c26dcb2b))
+- further filter improvements ([#1617](https://github.com/waku-org/nwaku/issues/1617)) ([d920b973](https://github.com/waku-org/nwaku/commit/d920b973))
+- **common:** added extensible implementation of the enr typed record ([ac56e1dc](https://github.com/waku-org/nwaku/commit/ac56e1dc))
+- **rln-relay:** fetch release from zerokit ci, or build ([#1603](https://github.com/waku-org/nwaku/issues/1603)) ([179be681](https://github.com/waku-org/nwaku/commit/179be681))
+- **filter-v2:** new filter protocol increment - message handling and clients ([#1600](https://github.com/waku-org/nwaku/issues/1600)) ([be446b98](https://github.com/waku-org/nwaku/commit/be446b98))
+
+### Fixes
+
+- **ci:** remove target flag from docker command ([#1725](https://github.com/waku-org/nwaku/issues/1725)) ([d822cdc5](https://github.com/waku-org/nwaku/commit/d822cdc5))
+- wakunode2 config. adding new 'topic' config parameter. ([#1727](https://github.com/waku-org/nwaku/issues/1727)) ([2ec9809c](https://github.com/waku-org/nwaku/commit/2ec9809c))
+- streams was used instead of connections ([#1722](https://github.com/waku-org/nwaku/issues/1722)) ([b9e0763e](https://github.com/waku-org/nwaku/commit/b9e0763e))
+- change filter request default behaviour to ping ([#1721](https://github.com/waku-org/nwaku/issues/1721)) ([7c39be9a](https://github.com/waku-org/nwaku/commit/7c39be9a))
+- **rln-relay:** handle invalid deletes ([#1717](https://github.com/waku-org/nwaku/issues/1717)) ([81dffee8](https://github.com/waku-org/nwaku/commit/81dffee8))
+- fix filter v2 proto fields ([#1716](https://github.com/waku-org/nwaku/issues/1716)) ([68a39c65](https://github.com/waku-org/nwaku/commit/68a39c65))
+- unstable peers in mesh ([#1710](https://github.com/waku-org/nwaku/issues/1710)) ([703c3ab5](https://github.com/waku-org/nwaku/commit/703c3ab5))
+- **networkmonitor:** break import dependency with wakunode2 app ([043feacd](https://github.com/waku-org/nwaku/commit/043feacd))
+- import nimchronos instead heartbeat ([#1695](https://github.com/waku-org/nwaku/issues/1695)) ([7d12adf6](https://github.com/waku-org/nwaku/commit/7d12adf6))
+- **rest:** change rest server result error type to string ([d5ef9331](https://github.com/waku-org/nwaku/commit/d5ef9331))
+- **rln-relay:** scope of getEvents ([#1672](https://github.com/waku-org/nwaku/issues/1672)) ([b62193e5](https://github.com/waku-org/nwaku/commit/b62193e5))
+- **logs:** fix log reporting wrong ok connected peers ([#1675](https://github.com/waku-org/nwaku/issues/1675)) ([1a885b96](https://github.com/waku-org/nwaku/commit/1a885b96))
+- move canBeConnected to PeerManager and check for potential overflow ([#1670](https://github.com/waku-org/nwaku/issues/1670)) ([d5c2770c](https://github.com/waku-org/nwaku/commit/d5c2770c))
+- wrap untracked protocol handler exceptions ([9e1432c9](https://github.com/waku-org/nwaku/commit/9e1432c9))
+- **wakunode2:** made setup nat return errors ([1cfb251b](https://github.com/waku-org/nwaku/commit/1cfb251b))
+- fixed multiple bare except warnings ([caf78249](https://github.com/waku-org/nwaku/commit/caf78249))
+- bump libp2p with traffic metrics fix ([#1642](https://github.com/waku-org/nwaku/issues/1642)) ([0ef46673](https://github.com/waku-org/nwaku/commit/0ef46673))
+- **rln-relay:** buildscript bad cp ([#1636](https://github.com/waku-org/nwaku/issues/1636)) ([bd9857c1](https://github.com/waku-org/nwaku/commit/bd9857c1))
+- **wakunode2:** fix main warnings and drop swap support ([f95147f5](https://github.com/waku-org/nwaku/commit/f95147f5))
+- **rln-relay:** on chain registration ([#1627](https://github.com/waku-org/nwaku/issues/1627)) ([b1bafda2](https://github.com/waku-org/nwaku/commit/b1bafda2))
+- connect instead of dialing relay peers ([#1622](https://github.com/waku-org/nwaku/issues/1622)) ([85f33a8e](https://github.com/waku-org/nwaku/commit/85f33a8e))
+- fix hash size greater than 32 ([#1621](https://github.com/waku-org/nwaku/issues/1621)) ([c42ac16f](https://github.com/waku-org/nwaku/commit/c42ac16f))
+
+### Changes
+
+- **ci:** cache all of submodules/deps to speed up build time ([#1731](https://github.com/waku-org/nwaku/issues/1731)) ([4394c69d](https://github.com/waku-org/nwaku/commit/4394c69d))
+- **rln-relay:** update args to contract ([#1724](https://github.com/waku-org/nwaku/issues/1724)) ([b277ce10](https://github.com/waku-org/nwaku/commit/b277ce10))
+- **rln-relay:** use new config for ffi ([#1718](https://github.com/waku-org/nwaku/issues/1718)) ([44c54312](https://github.com/waku-org/nwaku/commit/44c54312))
+- adding new tutorial on how to handle heaptrack with nim waku ([#1719](https://github.com/waku-org/nwaku/issues/1719)) ([4b59e472](https://github.com/waku-org/nwaku/commit/4b59e472))
+- add timestamp and ephemeral for opt-in dos validator ([#1713](https://github.com/waku-org/nwaku/issues/1713)) ([3e0a693d](https://github.com/waku-org/nwaku/commit/3e0a693d))
+- add test vectors dos protection validator ([#1711](https://github.com/waku-org/nwaku/issues/1711)) ([eaa162ee](https://github.com/waku-org/nwaku/commit/eaa162ee))
+- add validator for dos protec metrics and move to app ([#1704](https://github.com/waku-org/nwaku/issues/1704)) ([3e146869](https://github.com/waku-org/nwaku/commit/3e146869))
+- use QUICK_AND_DIRTY_COMPILER flag for CI ([#1708](https://github.com/waku-org/nwaku/issues/1708)) ([21510425](https://github.com/waku-org/nwaku/commit/21510425))
+- move networkmonitor and wakucanary to apps directory ([209579b0](https://github.com/waku-org/nwaku/commit/209579b0))
+- **wakunode2:** flatten and simplify app setup ([#1705](https://github.com/waku-org/nwaku/issues/1705)) ([ce92fc1a](https://github.com/waku-org/nwaku/commit/ce92fc1a))
+- **wakunode2:** split setup logic into app module ([c8081c88](https://github.com/waku-org/nwaku/commit/c8081c88))
+- add payload bytes to trace log ([#1703](https://github.com/waku-org/nwaku/issues/1703)) ([c6d291d3](https://github.com/waku-org/nwaku/commit/c6d291d3))
+- refactor flaky test with while ([#1698](https://github.com/waku-org/nwaku/issues/1698)) ([dca0e9b2](https://github.com/waku-org/nwaku/commit/dca0e9b2))
+- **core:** move peers utils module to waku_core ([e041e043](https://github.com/waku-org/nwaku/commit/e041e043))
+- decouple test2 target from testcommon ([91baa232](https://github.com/waku-org/nwaku/commit/91baa232))
+- **core:** move utils time module to waku_core ([93b0c071](https://github.com/waku-org/nwaku/commit/93b0c071))
+- add deprecation notice to utils module. move heartbeat to common ([e8dceb2a](https://github.com/waku-org/nwaku/commit/e8dceb2a))
+- **core:** rename waku_message module to waku_core ([c9b6b230](https://github.com/waku-org/nwaku/commit/c9b6b230))
+- flatten waku v2 protocols folder ([d7b72ac7](https://github.com/waku-org/nwaku/commit/d7b72ac7))
+- fix test failing intermittently ([#1679](https://github.com/waku-org/nwaku/issues/1679)) ([8d213e85](https://github.com/waku-org/nwaku/commit/8d213e85))
+- **networking:** get relay number of connections from protocol conns/streams ([#1609](https://github.com/waku-org/nwaku/issues/1609)) ([73cbafa6](https://github.com/waku-org/nwaku/commit/73cbafa6))
+- allow to call store api endpoints without a storenode ([#1575](https://github.com/waku-org/nwaku/issues/1575)) ([#1647](https://github.com/waku-org/nwaku/issues/1647)) ([0b4a2e68](https://github.com/waku-org/nwaku/commit/0b4a2e68))
+- bump container image versions to v0.16.0 in quickstart ([#1640](https://github.com/waku-org/nwaku/issues/1640)) ([5c33d9d1](https://github.com/waku-org/nwaku/commit/5c33d9d1))
+- **node:** remove deprecated constructor and extend testlib with builder ([9dadc1f5](https://github.com/waku-org/nwaku/commit/9dadc1f5))
+- do not mount relay more than once ([#1650](https://github.com/waku-org/nwaku/issues/1650)) ([5d853b86](https://github.com/waku-org/nwaku/commit/5d853b86))
+- pointed all waku node imports to the barrel import ([e8448dfd](https://github.com/waku-org/nwaku/commit/e8448dfd))
+- **node:** added waku_node barrel import and split config module ([13942888](https://github.com/waku-org/nwaku/commit/13942888))
+- remove deprecated enr record init method ([0627b4f8](https://github.com/waku-org/nwaku/commit/0627b4f8))
+- **deps:** upgrade nim-chronos and nim-presto to latest version ([7c229ece](https://github.com/waku-org/nwaku/commit/7c229ece))
+- remove waku swap protocol ([2b5fd2a2](https://github.com/waku-org/nwaku/commit/2b5fd2a2))
+- **deps:** upgrade nim-confutils to latest version ([67fa736d](https://github.com/waku-org/nwaku/commit/67fa736d))
+- **rln-relay:** gracefully handle chain forks ([#1623](https://github.com/waku-org/nwaku/issues/1623)) ([00a3812b](https://github.com/waku-org/nwaku/commit/00a3812b))
+- bump nim-libp2p 53b060f ([#1633](https://github.com/waku-org/nwaku/issues/1633)) ([11ff93c2](https://github.com/waku-org/nwaku/commit/11ff93c2))
+- added testcommon target to makefile ([048ca45d](https://github.com/waku-org/nwaku/commit/048ca45d))
+- increase meta size to 64 bytes + tests ([#1629](https://github.com/waku-org/nwaku/issues/1629)) ([1f793756](https://github.com/waku-org/nwaku/commit/1f793756))
+- **enr:** move waku enr multiaddr to typedrecord and builder extensions ([2ffd2f80](https://github.com/waku-org/nwaku/commit/2ffd2f80))
+- **enr:** added waku2 capabilities accessor ([157724d9](https://github.com/waku-org/nwaku/commit/157724d9))
+- **rln-relay:** reduce exports ([#1615](https://github.com/waku-org/nwaku/issues/1615)) ([2f3ba3d6](https://github.com/waku-org/nwaku/commit/2f3ba3d6))
+- add dash between target and version ([#1613](https://github.com/waku-org/nwaku/issues/1613)) ([24d62791](https://github.com/waku-org/nwaku/commit/24d62791))
+- **release:** added regression checking and clarifications ([#1610](https://github.com/waku-org/nwaku/issues/1610)) ([b495dd7b](https://github.com/waku-org/nwaku/commit/b495dd7b))
+
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1`
`/vac/waku/filter-subscribe/2.0.0-beta1`
`/vac/waku/filter-push/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## Upgrade instructions
+
+* The `--topics` config option has been deprecated to unify the configuration style. It still available in this and will be in next release, but will be removed after that. The new option `--topic` is introduced, which can be use repeatedly to achieve the same behaviour.
+
+## 2023-03-15 v0.16.0
+
+## What's Changed
+
+Release highlights:
+- a fix for an issue that prevented the node from generating high-resolution (up to nanosecond) timestamps
+- introduction of an application-defined `meta` attribute to the Waku Message. This can be quite valuable for network-wide deduplication, deterministic hashing, validity checking and other planned improvements to the protocol
+- many optimizations in RLN implementation and its underlying dependencies
+
+### Features
+
+- Integrated a new group manager for RLN-protected relay [1496](https://github.com/waku-org/nwaku/pull/1496)
+- Added application-defined meta attribute to Waku Message according to RFC [14/WAKU2-MESSAGE](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/14/message.md#message-attributes) [1581](https://github.com/waku-org/nwaku/pull/1581)
+- Implemented deterministic hashing scheme for Waku Messages according to RFC [14/WAKU2-MESSAGE](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/14/message.md#deterministic-message-hashing) [1586](https://github.com/waku-org/nwaku/pull/1586)
+
+### Changes
+
+- Upgraded nim-sqlite3-abi to the latest version [1565](https://github.com/waku-org/nwaku/pull/1565)
+- Better validation of protocol buffers [1563](https://github.com/waku-org/nwaku/pull/1563)
+- Improved underlying Zerokit performance and FFI [1571](https://github.com/waku-org/nwaku/pull/1571)
+- Node peer ID now logged with relay trace logging [1574](https://github.com/waku-org/nwaku/pull/1574)
+- Continued refactoring of several protocol implementations to improve maintainability and readability
+- Refactored and cleaned up peer manager [1539](https://github.com/waku-org/nwaku/pull/1539)
+- Removed unused and legacy websocket submodule [1580](https://github.com/waku-org/nwaku/pull/1580) [1582](https://github.com/waku-org/nwaku/pull/1582)
+- Use base64 URL-safe encoding for noise [1569](https://github.com/waku-org/nwaku/pull/1569)
+- Various general improvements to RLN implementation [1585](https://github.com/waku-org/nwaku/pull/1585) [1587](https://github.com/waku-org/nwaku/pull/1587)
+- Started on implementation for new and improved filter protocol [1584](https://github.com/waku-org/nwaku/pull/1584)
+- Updated pubsub and content topic namespacing to reflect latest changes in RFC [23/WAKU2-TOPICS](https://github.com/vacp2p/rfc-index/blob/main/waku/informational/23/topics.md) [1589](https://github.com/waku-org/nwaku/pull/1589)
+- Unified internal peer data models [1597](https://github.com/waku-org/nwaku/pull/1597)
+- Improved internal implementation of Waku ENR encoding and decoding [1598](https://github.com/waku-org/nwaku/pull/1598) [1599](https://github.com/waku-org/nwaku/pull/1599)
+- Underlying dependency for RLN implementation now loaded as a static library [1578](https://github.com/waku-org/nwaku/pull/1578)
+
+### Fixes
+
+- Fixed internally generated timestamps to allow higher resolution than seconds [1570](https://github.com/waku-org/nwaku/pull/1570)
+- Fixed padded base64 usage for encoding and decoding payloads on the JSON RPC API [1572](https://github.com/waku-org/nwaku/pull/1572)
+- Fixed incorrect relative module imports [1591](https://github.com/waku-org/nwaku/pull/1591)
+- Fixed RLN relay erroneously storing messages from multiple apps [1594](https://github.com/waku-org/nwaku/pull/1594)
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2023-02-15 v0.15.0
+
+Release highlights:
+- Relay connectivity is now maintained by a management loop that selects from the peerstore
+- Ability to manually specify `multiaddrs` for the nwaku node to advertise
+- Two important fixes related to historical message queries:
+ - fixed archive bug that resulted in duplicate messages in store query response
+ - fixed query page size limit not being respected
+
+### Features
+
+- New connectivity loop to maintain relay connectivity from peerstore [1482](https://github.com/waku-org/nwaku/pull/1482) [1462](https://github.com/waku-org/nwaku/pull/1462)
+- Support for manually specifying `multiaddrs` to advertise [1509](https://github.com/waku-org/nwaku/pull/1509) [1512](https://github.com/waku-org/nwaku/pull/1512)
+- Added dynamic keystore for membership credential storage and management [1466](https://github.com/waku-org/nwaku/pull/1466)
+
+### Changes
+
+- Abstracted RLN relay group management into its own API [1465](https://github.com/waku-org/nwaku/pull/1465)
+- Prune peers from peerstore when exceeding capacity [1513](https://github.com/waku-org/nwaku/pull/1513)
+- Removed Kilic submodule [1517](https://github.com/waku-org/nwaku/pull/1517)
+- Continued refactoring of several protocol implementations to improve maintainability and readability
+- Refactored and improved JSON RPC API
+- Added safe default values for peer-store-capacity [1525](https://github.com/waku-org/nwaku/pull/1525)
+- Improvements in regular CI test reliability and repeatability
+- Improved archive query performance [1510](https://github.com/waku-org/nwaku/pull/1510)
+- Added better e2e trace logging for relay messages [1526](https://github.com/waku-org/nwaku/pull/1526)
+- Relay RPC API now encodes message payloads in base64 [572](https://github.com/vacp2p/rfc/pull/572) [1555](https://github.com/waku-org/nwaku/pull/1555)
+
+### Fixes
+
+- Fixed Waku archive queries returning duplicate messages due to incorrect reordering [1511](https://github.com/waku-org/nwaku/pull/1511)
+- Fixed Admin RPC API crashing on returning peer with no multiaddresses [1507](https://github.com/waku-org/nwaku/pull/1507)
+- Fixed page size limit not being respected in store query responses [1520](https://github.com/waku-org/nwaku/pull/1520)
+- Fixed nwaku subscribing to default pubsub topic even if not configured [1548](https://github.com/waku-org/nwaku/pull/1548)
+- Fixed underlying issue causing node to incorrectly report it's unreachable [1518](https://github.com/waku-org/nwaku/pull/1518) [1546](https://github.com/waku-org/nwaku/pull/1546)
+- Fixed Relay RPC API not adhering to RFC [1139](https://github.com/waku-org/nwaku/issues/1139)
+- Fixed message IDs in nwaku diverging from those in go-waku [1556](https://github.com/waku-org/nwaku/pull/1556)
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2023-01-16 v0.14.0
+
+Release highlights:
+- An important fix for the Waku message archive returning inconsistent responses to history queries.
+- Support for [AutoNAT](https://docs.libp2p.io/concepts/nat/autonat/) and [libp2p Circuit Relay](https://docs.libp2p.io/concepts/nat/circuit-relay/) that allows, among other things, for [NAT hole punching](https://docs.libp2p.io/concepts/nat/hole-punching/).
+- Support for structured logging in JSON format.
+- A fix for an underlying file descriptor leak that affected websocket connections.
+
+### Features
+
+- Support for [AutoNAT](https://docs.libp2p.io/concepts/nat/autonat/)
+- Support for [libp2p Circuit Relay](https://docs.libp2p.io/concepts/nat/circuit-relay/) (server only)
+- New Waku Archive implementation. This allows easy addition of drivers for different technologies to store historical messages.
+- Support for structured logging and specifying log format.
+- Node now keeps track of its external reachability.
+
+### Changes
+
+- Zerokit RLN library now statically linked.
+- Use extended key generation in Zerokit API to comply with [32/RLN](https://github.com/vacp2p/rfc-index/blob/main/vac/32/rln-v1.md).
+- Re-enable root validation in [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) implementation.
+- [Network monitoring tool](https://github.com/status-im/nwaku/tree/2336522d7f478337237a5a4ec8c5702fb4babc7d/tools#networkmonitor) now supports DNS discovery.
+- Added [dashboard](https://github.com/waku-org/nwaku/blob/3e0e1cb2398297fca761aa74f52d32fa837d556c/metrics/waku-network-monitor-dashboard.json) for network monitoring.
+- Continued refactoring of several protocol implementations to improve maintainability and readability.
+- Removed swap integration from store protocol.
+- Peerstore now consolidated with libp2p peerstore.
+- Peerstore now also tracks peer direction.
+- SIGSEGV signals are now handled and logged properly.
+- Waku v2 no longer imports libraries from Waku v1.
+- Improved build and CI processes:
+ - Added support for an `EXPERIMENTAL` compiler flag.
+ - Simplified project Makefile.
+ - Split Dockerfile into production and experimental stages.
+ - Removed obsolete simulation libraries from build.
+- Improved parallellisation (and therefore processing time) when dialing several peers simultaneously.
+- Waku Archive now responds with error to historical queries containing more than 10 content topics.
+
+### Fixes
+
+- Fixed support for optional fields in several protocol rpc codecs. [#1393](https://github.com/waku-org/nwaku/pull/1393) [#1395](https://github.com/waku-org/nwaku/pull/1395) [#1396](https://github.com/waku-org/nwaku/pull/1396)
+- Fixed clients with `--store=false` not installing Store Client JSON-RPC API handlers. [#1382](https://github.com/waku-org/nwaku/pull/1382)
+- Fixed SQLite driver returning inconsistent responses to store queries. [#1415](https://github.com/waku-org/nwaku/pull/1415)
+- Fixed peer exchange discv5 loop starting before discv5 has started. [#1407](https://github.com/waku-org/nwaku/pull/1407)
+- Fixed wakubridge test timing. [#1429](https://github.com/waku-org/nwaku/pull/1429)
+- Fixed bug in Noise module types equating `T_ss` incorrectly to `"se"` and not `"ss"`. [#1432](https://github.com/waku-org/nwaku/pull/1432)
+- Fixed Ctrl-C quitting resulting in unreleased resources and exit failures. [#1416](https://github.com/waku-org/nwaku/pull/1416)
+- Fixed CI workflows not cloning repo on startup. [#1454](https://github.com/waku-org/nwaku/pull/1454) [#1455](https://github.com/waku-org/nwaku/pull/1455)
+- Fixed Admin API peer connection not returning error response if peer can't be connected. [#1476](https://github.com/waku-org/nwaku/pull/1476)
+- Fixed underlying file descriptor leak. [#1483](https://github.com/waku-org/nwaku/pull/1483)
+
+### Docs
+
+- Added [instructions](https://github.com/waku-org/nwaku/blob/3e0e1cb2398297fca761aa74f52d32fa837d556c/docs/operators/quickstart.md) for running nwaku with docker compose.
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2022-11-15 v0.13.0
+
+Release highlights:
+- A [Waku canary tool](https://github.com/status-im/nwaku/tree/2336522d7f478337237a5a4ec8c5702fb4babc7d/tools#waku-canary-tool) to check if nodes are reachable and what protocols they support.
+- Simplified configuration for store protocol. This [new guide](https://github.com/status-im/nwaku/blob/4e5318bfbb204bd1239c95472d7b84b6a326dd9d/docs/operators/how-to/configure-store.md) explains how to configure store from this release forward.
+- Support for environment variables to configure a nwaku node. See our [configuration guide](https://github.com/status-im/nwaku/blob/384abed614050bf3aa90c901d7f5e8bc383e8b22/docs/operators/how-to/configure.md) for more.
+- A Waku [network monitoring tool](https://github.com/status-im/nwaku/tree/2336522d7f478337237a5a4ec8c5702fb4babc7d/tools#networkmonitor) to report network metrics, including network size, discoverable peer capabilities and more.
+
+### Features
+
+- Added Waku canary tool to check if i) a given node is reachable and ii) it supports a set of protocols.
+- Simplified [Waku store configuration](https://github.com/status-im/nwaku/blob/4e5318bfbb204bd1239c95472d7b84b6a326dd9d/docs/operators/how-to/configure-store.md).
+- Decoupled Waku peer persistence configuration from message store configuration.
+- Added keyfile support for secure storage of RLN credentials.
+- Added configurable libp2p agent string to nwaku switch.
+- Support for [configuration with environment variables](https://github.com/status-im/nwaku/blob/384abed614050bf3aa90c901d7f5e8bc383e8b22/docs/operators/how-to/configure.md).
+- Added [example module](https://github.com/status-im/nwaku/tree/2336522d7f478337237a5a4ec8c5702fb4babc7d/examples/v2) to showcase basic nwaku relay usage.
+- Added a nwaku [network monitoring tool](https://github.com/status-im/nwaku/tree/2336522d7f478337237a5a4ec8c5702fb4babc7d/tools#networkmonitor) to provide metrics on peers, network size and more.
+
+### Changes
+
+- Removed support for Kilic's RLN library (obsolete).
+- Improved logging for [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) implementation.
+- Connection to eth node for RLN now more stable, maintains state and logs failures.
+- Waku apps and tools now moved to their own subdirectory.
+- Continued refactoring of several protocol implementations to improve maintainability and readability.
+- Periodically log metrics when running RLN spam protection.
+- Added metrics dashboard for RLN spam protection.
+- Github CI test workflows are now run selectively, based on the content of a PR.
+- Improved reliability of CI runs and added email notifications.
+- Discv5 discovery loop now triggered to fill a [34/WAKU2-PEER-EXCHANGE](https://github.com/waku-org/specs/blob/master/standards/core/peer-exchange.md) peer list cache asynchronously.
+- Upgraded to Nim v1.6.6.
+- Cleaned up compiler warnings on unused imports.
+- Improved exception handling and annotation.
+- [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) no longer enabled by default on nwaku nodes.
+- Merkle tree roots for RLN membership changes now on a per-block basis to allow poorly connected peers to operate within a window of acceptable roots.
+
+### Fixes
+
+- Fixed encoding of ID commitments for RLN from Big-Endian to Little-Endian. [#1256](https://github.com/status-im/nwaku/pull/1256)
+- Fixed maxEpochGap to be the maximum allowed epoch gap (RLN). [#1257](https://github.com/status-im/nwaku/pull/1257)
+- Fixed store cursors being retrieved incorrectly (truncated) from DB. [#1263](https://github.com/status-im/nwaku/pull/1263)
+- Fixed message indexed by store cursor being excluded from history query results. [#1263](https://github.com/status-im/nwaku/pull/1263)
+- Fixed log-level configuration being ignored by the nwaku node. [#1272](https://github.com/status-im/nwaku/pull/1272)
+- Fixed incorrect error message when failing to set [34/WAKU2-PEER-EXCHANGE](https://github.com/waku-org/specs/blob/master/standards/core/peer-exchange.md) peer. [#1298](https://github.com/status-im/nwaku/pull/1298)
+- Fixed and replaced deprecated `TaintedString` type. [#1326](https://github.com/status-im/nwaku/pull/1326)
+- Fixed and replaced unreliable regex library and usage. [#1327](https://github.com/status-im/nwaku/pull/1327) [#1328](https://github.com/status-im/nwaku/pull/1328)
+- Fixed and replaced deprecated `ganache-cli` node package with `ganache` for RLN onchain tests. Added graceful daemon termination. [#1347](https://github.com/status-im/nwaku/pull/1347)
+
+### Docs
+
+- Added cross client RLN testnet [tutorial](https://github.com/status-im/nwaku/blob/44d8a2026dc31a37e181043ceb67e2822376dc03/docs/tutorial/rln-chat-cross-client.md).
+- Fixed broken link to Kibana in [cluster documentation](https://github.com/status-im/nwaku/blob/5e90085242e9e4d6f3cf307e189efbf7e59da9f9/docs/contributors/cluster-logs.md).
+- Added an improved [quickstart guide](https://github.com/status-im/nwaku/blob/8f5363ea8f5e95fc1104307aa0d2fc59fda13698/docs/operators/quickstart.md) for operators.
+- Added a [Docker usage guide](https://github.com/status-im/nwaku/blob/8f5363ea8f5e95fc1104307aa0d2fc59fda13698/docs/operators/docker-quickstart.md#prerequisites) for operators.
+- Added operator [guide on running RLN spam prevention](https://github.com/status-im/nwaku/blob/bd516788cb39132ccbf0a4dcf0880e9694beb233/docs/operators/how-to/run-with-rln.md) on nwaku nodes.
+- Extended guidelines on nwaku [configuration methods](https://github.com/status-im/nwaku/blob/384abed614050bf3aa90c901d7f5e8bc383e8b22/docs/operators/how-to/configure.md) for operators.
+- Added new [store configuration guide](https://github.com/status-im/nwaku/blob/4e5318bfbb204bd1239c95472d7b84b6a326dd9d/docs/operators/how-to/configure-store.md) to reflect simplified options.
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2022-10-06 v0.12.0
+
+Release highlights:
+- The performance and stability of the message `store` has improved dramatically. Query durations, even for long-term stores, have improved by more than a factor of 10.
+- Support for Waku Peer Exchange - a discovery method for resource-restricted nodes.
+- Messages can now be marked as "ephemeral" to prevent them from being stored.
+- [Zerokit](https://github.com/vacp2p/zerokit) is now the default implementation for spam-protected `relay` with RLN.
+
+The full list of changes is below.
+
+### Features
+
+- Default support for [Zerokit](https://github.com/vacp2p/zerokit) version of [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) implementation.
+- Added Filter REST API OpenAPI specification.
+- Added POC implementation for [43/WAKU2-DEVICE-PAIRING](https://github.com/waku-org/specs/blob/master/standards/application/device-pairing.md).
+- [14/WAKU2-MESSAGE](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/14/message.md) can now be marked as `ephemeral` to prevent them from being stored.
+- Support for [34/WAKU2-PEER-EXCHANGE](https://github.com/waku-org/specs/blob/master/standards/core/peer-exchange.md).
+
+### Changes
+
+- [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) implementation now handles on-chain transaction errors.
+- [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) implementation now validates the Merkle tree root against a window of acceptable roots.
+- Added metrics for [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) implementation.
+- Continued refactoring of several protocol implementations to improve maintainability and readability.
+- Cleaned up nwaku imports and dependencies.
+- Refactored and organised nwaku unit tests.
+- Nwaku now periodically logs node metrics by default.
+- Further improvements to the `store` implementation:
+ - Better logging and query traceability.
+ - More useful metrics to measure query and insertion time.
+ - Reworked indexing for faster inserts and queries.
+ - Reworked data model to use a simple, single timestamp for indexing, ordering and querying.
+ - Improved retention policy management with periodic execution.
+ - Run sqlite database vacuum at node start.
+ - Improved logging when migrating the database to a newer version.
+- `relay` no longer auto-mounted on all nwaku nodes.
+- The most complete node ENR now included in response to API requests for node `info()`.
+- Updated Grafana dashboards included with nwaku.
+- Github CI test execution now skipped for doc-only changes.
+
+### Fixes
+
+- Fixed nwaku unnecessary sleep when no dynamic bootstrap nodes retrieved.
+- Fixed [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) not working from browser-based clients due to nwaku peer manager failing to reuse existing connection.
+- Waku Message payload now correctly encoded as base64 in the Relay REST API.
+- Fixed handling of bindParam(uint32) in sqlite.
+- `chat2` application now correctly selects a random store node on startup.
+- Fixed macos builds failing due to an unsupported dependency.
+- Fixed nwaku not reconnecting to previously discovered nodes after losing connection.
+- Fixed nwaku failing to start switch transports with external IP configuration.
+- Fixed SIGSEGV crash when attempting to start nwaku store without `db-path` configuration.
+
+### Docs
+
+- Improved [RLN testnet tutorial](https://github.com/status-im/nwaku/blob/14abdef79677ddc828ff396ece321e05cedfca17/docs/tutorial/onchain-rln-relay-chat2.md)
+- Added [tutorial](https://github.com/status-im/nwaku/blob/14abdef79677ddc828ff396ece321e05cedfca17/docs/operators/droplet-quickstart.md) on running nwaku from a DigitalOcean droplet.
+- Added [guide](https://github.com/status-im/nwaku/blob/14abdef79677ddc828ff396ece321e05cedfca17/docs/operators/how-to/monitor.md) on how to monitor nwaku using Prometheus and Grafana.
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2022-08-15 v0.11
+
+Release highlights:
+- Major improvements in the performance of historical message queries to longer-term, sqlite-only message stores.
+- Introduction of an HTTP REST API with basic functionality
+- On-chain RLN group management. This was also integrated into an [example spam-protected chat application](https://github.com/status-im/nwaku/blob/4f93510fc9a938954dd85593f8dc4135a1c367de/docs/tutorial/onchain-rln-relay-chat2.md).
+
+The full list of changes is below.
+
+### Features
+
+- Support for on-chain group membership management in the [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) implementation.
+- Integrated HTTP REST API for external access to some `wakunode2` functionality:
+ - Debug REST API exposes debug information about a `wakunode2`.
+ - Relay REST API allows basic pub/sub functionality according to [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md).
+- [`35/WAKU2-NOISE`](https://github.com/waku-org/specs/blob/master/standards/application/noise.md) implementation now adds padding to ChaChaPoly encryptions to increase security and reduce metadata leakage.
+
+### Changes
+
+- Significantly improved the SQLite-only historical message `store` query performance.
+- Refactored several protocol implementations to improve maintainability and readability.
+- Major code reorganization for the [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) implementation to improve maintainability. This will also make the `store` extensible to support multiple implementations.
+- Disabled compiler log colors when running in a CI environment.
+- Refactored [`35/WAKU2-NOISE`](https://github.com/waku-org/specs/blob/master/standards/application/noise.md) implementation into smaller submodules.
+- [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) implementation can now optionally be compiled with [Zerokit RLN](https://github.com/vacp2p/zerokit/tree/64f508363946b15ac6c52f8b59d8a739a33313ec/rln). Previously only [Kilic's RLN](https://github.com/kilic/rln/tree/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43) was supported.
+
+### Fixes
+
+- Fixed wire encoding of protocol buffers to use proto3.
+- Fixed Waku v1 <> Waku v2 bridge losing connection to statically configured v1 nodes.
+- Fixed underlying issue causing DNS discovery to fail for records containing multiple strings.
+
+### Docs
+
+- Updated [release process](https://github.com/status-im/nwaku/blob/4f93510fc9a938954dd85593f8dc4135a1c367de/docs/contributors/release-process.md) documentation.
+- Added [tutorial](https://github.com/status-im/nwaku/blob/4f93510fc9a938954dd85593f8dc4135a1c367de/docs/tutorial/onchain-rln-relay-chat2.md) on how to run a spam-protected chat2 application with on-chain group management.
+
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2022-06-15 v0.10
+
+Release highlights:
+- Support for key exchange using Noise handshakes.
+- Support for a SQLite-only historical message `store`. This allows for cheaper, longer-term historical message storage on disk rather than in memory.
+- Several fixes for native WebSockets, including slow or hanging connections and connections dropping unexpectedly due to timeouts.
+- A fix for a memory leak in nodes running a local SQLite database.
+
+### Features
+
+- Support for [`35/WAKU2-NOISE`](https://github.com/waku-org/specs/blob/master/standards/application/noise.md) handshakes as key exchange protocols.
+- Support for TOML config files via `--config-file=`.
+- Support for `--version` command. This prints the current tagged version (or compiled commit hash, if not on a version).
+- Support for running `store` protocol from a `filter` client, storing only the filtered messages.
+- Start of an HTTP REST API implementation.
+- Support for a memory-efficient SQLite-only `store` configuration.
+
+### Changes
+
+- Added index on `receiverTimestamp` in the SQLite `store` to improve query performance.
+- GossipSub [Peer Exchange](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#prune-backoff-and-peer-exchange) is now disabled by default. This is a more secure option.
+- Progress towards dynamic group management for the [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) implementation.
+- Nodes with `--keep-alive` enabled now sends more regular pings to keep connections more reliably alive.
+- Disabled `swap` protocol by default.
+- Reduced unnecessary and confusing logging, especially during startup.
+- Added discv5 UDP port to the node's main discoverable ENR.
+
+### Fixes
+
+- The in-memory `store` now checks the validity of message timestamps before storing.
+- Fixed underlying bug that caused connection leaks in the HTTP client.
+- Fixed Docker image compilation to use the correct external variable for compile-time flags (`NIMFLAGS` instead of `NIM_PARAMS`).
+- Fixed issue where `--dns4-domain-name` caused an unhandled exception if no external port was available.
+- Avoids unnecessarily calling DB migration if a `--db-path` is set but nothing is persisted in the DB. This led to a misleading warning log.
+- Fixed underlying issues that caused WebSocket connections to hang.
+- Fixed underlying issue that caused WebSocket connections to time out after 10 mins.
+- Fixed memory leak in nodes that implements a SQLite database.
+
+### Docs
+
+- Added [tutorial](https://github.com/status-im/nwaku/blob/16dd267bd9d25ff24c64fc5c92a20eb0d322217c/docs/operators/how-to/configure-key.md) on how to generate and configure a node key.
+- Added first [guide](https://github.com/status-im/nwaku/tree/16dd267bd9d25ff24c64fc5c92a20eb0d322217c/docs/operators) for nwaku operators.
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2022-03-31 v0.9
+
+Release highlights:
+
+- Support for Peer Exchange (PX) when a peer prunes a [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) mesh due to oversubscription. This can significantly increase mesh stability.
+- Improved start-up times through managing the size of the underlying persistent message storage.
+- New websocket connections are no longer blocked due to parsing failures in other connections.
+
+The full list of changes is below.
+
+### Features
+
+- Support for bootstrapping [`33/WAKU-DISCV5`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/33/discv5.md) via [DNS discovery](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/10/waku2.md#discovery-methods)
+- Support for GossipSub [Peer Exchange](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#prune-backoff-and-peer-exchange)
+
+### Changes
+
+- Waku v1 <> v2 bridge now supports DNS `multiaddrs`
+- Waku v1 <> v2 bridge now validates content topics before attempting to bridge a message from Waku v2 to Waku v1
+- Persistent message storage now auto deletes messages once over specified `--store-capacity`. This can significantly improve node start-up times.
+- Renamed Waku v1 <> v2 bridge `make` target and binary to `wakubridge`
+- Increased `store` logging to assist with debugging
+- Increased `rln-relay` logging to assist with debugging
+- Message metrics no longer include the content topic as a dimension to keep Prometheus metric cardinality under control
+- Waku v2 `toy-chat` application now sets the sender timestamp when creating messages
+- The type of the `proof` field of the `WakuMessage` is changed to `RateLimitProof`
+- Added method to the JSON-RPC API that returns the git tag and commit hash of the binary
+- The node's ENR is now included in the JSON-RPC API response when requesting node info
+
+### Fixes
+
+- Fixed incorrect conversion of seconds to nanosecond timestamps
+- Fixed store queries blocking due to failure in resource clean up
+- Fixed underlying issue where new websocket connections are blocked due to parsing failures in other connections
+- Fixed failure to log the ENR necessary for a discv5 connection to the node
+
+### Docs
+
+- Added [RAM requirements](https://github.com/status-im/nim-waku/tree/ee96705c7fbe4063b780ac43b7edee2f6c4e351b/waku/v2#wakunode) to `wakunode2` build instructions
+- Added [tutorial](https://github.com/status-im/nim-waku/blob/ee96705c7fbe4063b780ac43b7edee2f6c4e351b/docs/tutorial/rln-chat2-live-testnet.md) on communicating with waku2 test fleets via the chat2 `toy-chat` application in spam-protected mode using [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md).
+- Added a [section on bug reporting](https://github.com/status-im/nim-waku/blob/ee96705c7fbe4063b780ac43b7edee2f6c4e351b/README.md#bugs-questions--features) to `wakunode2` README
+- Fixed broken links in the [JSON-RPC API Tutorial](https://github.com/status-im/nim-waku/blob/5ceef37e15a15c52cbc589f0b366018e81a958ef/docs/tutorial/jsonrpc-api.md)
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2022-03-03 v0.8
+
+Release highlights:
+
+- Working demonstration and integration of [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) in the Waku v2 `toy-chat` application
+- Beta support for ambient peer discovery using [a version of Discovery v5](https://github.com/vacp2p/rfc/pull/487)
+- A fix for the issue that caused a `store` node to run out of memory after serving a number of historical queries
+- Ability to configure a `dns4` domain name for a node and resolve other dns-based `multiaddrs`
+
+The full list of changes is below.
+
+### Features
+
+- [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) implementation now supports spam-protection for a specific combination of `pubsubTopic` and `contentTopic` (available under the `rln` compiler flag).
+- [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) integrated into chat2 `toy-chat` (available under the `rln` compiler flag)
+- Added support for resolving dns-based `multiaddrs`
+- A Waku v2 node can now be configured with a domain name and `dns4` `multiaddr`
+- Support for ambient peer discovery using [`33/WAKU-DISCV5`](https://github.com/vacp2p/rfc/pull/487)
+
+### Changes
+
+- Metrics: now monitoring content topics and the sources of new connections
+- Metrics: improved default fleet monitoring dashboard
+- Introduced a `Timestamp` type (currently an alias for int64).
+- All timestamps changed to nanosecond resolution.
+- `timestamp` field number in WakuMessage object changed from `4` to `10`
+- [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) identifier updated to `/vac/waku/store/2.0.0-beta4`
+- `toy-chat` application now uses DNS discovery to connect to existing fleets
+
+### Fixes
+
+- Fixed underlying bug that caused occasional failures when reading the certificate for secure websockets
+- Fixed `store` memory usage issues when responding to history queries
+
+### Docs
+
+- Documented [use of domain certificates](https://github.com/status-im/nim-waku/tree/2972a5003568848164033da3fe0d7f52a3d54824/waku/v2#enabling-websocket) for secure websockets
+- Documented [how to configure a `dns4` domain name](https://github.com/status-im/nim-waku/tree/2972a5003568848164033da3fe0d7f52a3d54824/waku/v2#using-dns-discovery-to-connect-to-existing-nodes) for a node
+- Clarified [use of DNS discovery](https://github.com/status-im/nim-waku/tree/2972a5003568848164033da3fe0d7f52a3d54824/waku/v2#using-dns-discovery-to-connect-to-existing-nodes) and provided current URLs for discoverable fleet nodes
+- Added [tutorial](https://github.com/status-im/nim-waku/blob/2972a5003568848164033da3fe0d7f52a3d54824/docs/tutorial/rln-chat2-local-test.md) on using [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) with the chat2 `toy-chat` application
+- Added [tutorial](https://github.com/status-im/nim-waku/blob/2972a5003568848164033da3fe0d7f52a3d54824/docs/tutorial/bridge.md) on how to configure and a use a [`15/WAKU-BRIDGE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/15/bridge.md)
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2022-01-19 v0.7
+
+Release highlights:
+
+- Support for secure websockets.
+- Ability to remove unreachable clients in a `filter` node.
+- Several fixes to improve `store` performance and decrease query times. Query time for large stores decreased from longer than 8 min to under 100 ms.
+- Fix for a long-standing bug that prevented proper database migration in some deployed Docker containers.
+
+The full list of changes is below.
+
+### Features
+
+- Support for secure websocket transport
+
+### Changes
+
+- Filter nodes can now remove unreachable clients
+- The WakuInfo `listenStr` is deprecated and replaced with a sequence of `listenAddresses` to accommodate multiple transports
+- Removed cached `peerInfo` on local node. Rely on underlying libp2p switch instead
+- Metrics: added counters for protocol messages
+- Waku v2 node discovery now supports [`31/WAKU2-ENR`](https://github.com/waku-org/specs/blob/master/standards/core/enr.md)
+- resuming the history via `resume` now takes the answers of all peers in `peerList` into consideration and consolidates them into one deduplicated list
+
+### Fixes
+
+- Fixed database migration failure in the Docker image
+- All `HistoryResponse` messages are now auto-paginated to a maximum of 100 messages per response
+- Increased maximum length for reading from a libp2p input stream to allow largest possible protocol messages, including `HistoryResponse` messages at max size
+- Significantly improved `store` node query performance
+- Implemented a GossipSub `MessageIdProvider` for `11/WAKU2-RELAY` messages instead of relying on the unstable default
+- Receiver timestamps for message indexing in the `store` now have consistent millisecond resolution
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`17/WAKU-RLN-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) | `raw` | `/vac/waku/waku-rln-relay/2.0.0-alpha1` |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta3` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2021-11-05 v0.6
+
+Some useful features and fixes in this release, include:
+- two methods for Waku v2 node discovery
+- support for unsecure websockets, which paves the way for native browser usage
+- a fix for `nim-waku` store nodes running out of memory due to store size: the number of stored messages can now easily be configured
+- a fix for densely connected nodes refusing new connections: the maximum number of allowed connections can now easily be configured
+- support for larger message sizes (up from 64kb to 1Mb per message)
+
+The full list of changes is below.
+
+### Features
+
+- Waku v2 node discovery via DNS following [EIP-1459](https://eips.ethereum.org/EIPS/eip-1459)
+- Waku v2 node discovery via [Node Discovery v5](https://github.com/ethereum/devp2p/blob/master/discv5/discv5-theory.md)
+
+### Changes
+
+- Pagination of historical queries are now simplified
+- GossipSub [prune backoff period](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#prune-backoff-and-peer-exchange) is now the recommended 1 minute
+- Bridge now uses content topic format according to [23/WAKU2-TOPICS](https://github.com/vacp2p/rfc-index/blob/main/waku/informational/23/topics.md)
+- Better internal differentiation between local and remote peer info
+- Maximum number of libp2p connections is now configurable
+- `udp-port` CLI option has been removed for binaries where it's not used
+- Waku v2 now supports unsecure WebSockets
+- Waku v2 now supports larger message sizes of up to 1 Mb by default
+- Further experimental development of [RLN for spam protection](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md).
+These changes are disabled by default under a compiler flag. Changes include:
+ - Per-message rate limit proof defined
+ - RLN proof generation and verification integrated into Waku v2
+ - RLN tree depth changed from 32 to 20
+ - Support added for static membership group formation
+
+#### Docs
+
+- Added [contributor guidelines](https://github.com/status-im/nim-waku/blob/master/docs/contributors/waku-fleets.md) on Waku v2 fleet monitoring and management
+- Added [basic tutorial](https://github.com/status-im/nim-waku/blob/master/docs/tutorial/dns-disc.md) on using Waku v2 DNS-based discovery
+
+### Fixes
+
+- Bridge between `toy-chat` and matterbridge now shows correct announced addresses
+- Bridge no longer re-encodes already encoded payloads when publishing to V1
+- Bridge now populates WakuMessage timestamps when publishing to V2
+- Store now has a configurable maximum number of stored messages
+- Network simulations for Waku v1 and Waku v2 are runnable again
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`17/WAKU-RLN`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) | `raw` | `/vac/waku/waku-rln-relay/2.0.0-alpha1` |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta3` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2021-07-26 v0.5.1
+
+This patch release contains the following fix:
+- Support for multiple protocol IDs when reconnecting to previously connected peers:
+A bug in `v0.5` caused clients using persistent peer storage to only support the mounted protocol ID.
+
+This is a patch release that is fully backwards-compatible with release `v0.5`.
+It supports the same [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`17/WAKU-RLN`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) | `raw` | `/vac/waku/waku-rln-relay/2.0.0-alpha1` |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta3` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2021-07-23 v0.5
+
+This release contains the following:
+
+### Features
+- Support for keep-alives using [libp2p ping protocol](https://docs.libp2p.io/concepts/protocols/#ping).
+- DB migration for the message and peer stores.
+- Support for multiple protocol IDs. Mounted protocols now match versions of the same protocol that adds a postfix to the stable protocol ID.
+
+### Changes
+- Bridge topics are now configurable.
+- The `resume` Nim API now eliminates duplicates messages before storing them.
+- The `resume` Nim API now fetches historical messages in page sequence.
+- Added support for stable version of `relay` protocol, with protocol ID `/vac/waku/relay/2.0.0`.
+- Added optional `timestamp` to `WakuRelayMessage`.
+- Removed `PCRE` as a prerequisite for building Waku v1 and Waku v2.
+- Improved [`swap`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) metrics.
+
+#### General refactoring
+- Refactored modules according to [Nim best practices](https://hackmd.io/1imOGULZRsed2HpgmzGleA).
+- Simplified the [way protocols get notified](https://github.com/status-im/nim-waku/issues/574) of new messages.
+- Refactored `wakunode2` setup into 6 distinct phases with improved logging and error handling.
+- Moved `Whisper` types and protocol from the `nim-eth` module to `nim-waku`.
+
+#### Docs
+- Added [database migration tutorial](https://github.com/status-im/nim-waku/blob/master/docs/tutorial/db-migration.md).
+- Added [tutorial to setup `websockify`](https://github.com/status-im/nim-waku/blob/master/docs/tutorial/websocket.md).
+
+#### Schema
+- Updated the `Message` table of the persistent message store:
+ - Added `senderTimestamp` column.
+ - Renamed the `timestamp` column to `receiverTimestamp` and changes its type to `REAL`.
+
+#### API
+- Added optional `timestamp` to [`WakuRelayMessage`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/16/rpc.md) on JSON-RPC API.
+
+### Fixes
+- Conversion between topics for the Waku v1 <-> v2 bridge now follows the [RFC recommendation](https://github.com/vacp2p/rfc-index/blob/main/waku/informational/23/topics.md).
+- Fixed field order of `HistoryResponse` protobuf message: the field numbers of the `HistoryResponse` are shifted up by one to match up the [13/WAKU2-STORE](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) specs.
+
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`17/WAKU-RLN`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) | `raw` | `/vac/waku/waku-rln-relay/2.0.0-alpha1` |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta3` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2021-06-03 v0.4
+
+This release contains the following:
+
+### Features
+
+- Initial [`toy-chat` implementation](https://github.com/vacp2p/rfc-index/blob/main/waku/informational/22/toy-chat.md)
+
+### Changes
+
+- The [toy-chat application](https://github.com/status-im/nim-waku/blob/master/docs/tutorial/chat2.md) can now perform `lightpush` and request content-filtered messages from remote peers.
+- The [toy-chat application](https://github.com/status-im/nim-waku/blob/master/docs/tutorial/chat2.md) now uses default content topic `/toy-chat/2/huilong/proto`
+- Improve `toy-chat` [briding to matterbridge]((https://github.com/status-im/nim-waku/blob/master/docs/tutorial/chat2.md#bridge-messages-between-chat2-and-matterbridge))
+- Improve [`swap`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) logging and enable soft mode by default
+- Content topics are no longer in a redundant nested structure
+- Improve error handling
+
+#### API
+
+- [JSON-RPC Store API](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/16/rpc.md): Added an optional time-based query to filter historical messages.
+- [Nim API](https://github.com/status-im/nim-waku/blob/master/docs/api/v2/node.md): Added `resume` method.
+
+### Fixes
+
+- Connections between nodes no longer become unstable due to keep-alive errors if mesh grows large
+- Re-enable `lightpush` tests and fix Windows CI failure
+
+The [Waku v2 suite of protocols](https://github.com/waku-org/specs) are still in a raw/draft state.
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`17/WAKU-RLN`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) | `raw` | `/vac/waku/waku-rln-relay/2.0.0-alpha1` |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `draft` | `/vac/waku/relay/2.0.0-beta2` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta3` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `draft` | `/vac/waku/swap/2.0.0-beta1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2021-05-11 v0.3
+
+This release contains the following:
+
+### Features
+
+- Start of [`RLN relay` implementation](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md)
+- Start of [`swap` implementation](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md)
+- Start of [fault-tolerant `store` implementation](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/application/21/fault-tolerant-store.md)
+- Initial [`bridge` implementation](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/15/bridge.md) between Waku v1 and v2 protocols
+- Initial [`lightpush` implementation](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md)
+- A peer manager for `relay`, `filter`, `store` and `swap` peers
+- Persistent storage for peers: A node with this feature enabled will now attempt to reconnect to `relay` peers after a restart. It will respect the gossipsub [PRUNE backoff](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#prune-backoff-and-peer-exchange) period before attempting to do so.
+- `--persist-peers` CLI option to persist peers in local storage
+- `--persist-messages` CLI option to store historical messages locally
+- `--keep-alive` CLI option to maintain a stable connection to `relay` peers on idle topics
+- A CLI chat application ([`chat2`](https://github.com/status-im/nim-waku/blob/master/docs/tutorial/chat2.md)) over Waku v2 with [bridging to matterbridge](https://github.com/status-im/nim-waku/blob/master/docs/tutorial/chat2.md#bridge-messages-between-chat2-and-matterbridge)
+
+### Changes
+- Enable `swap` protocol by default and improve logging
+#### General refactoring
+
+- Split out `waku_types` types into the right place; create `utils` folder.
+- Change type of `contentTopic` in [`ContentFilter`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md#protobuf) to `string`.
+- Replace sequence of `contentTopics` in [`ContentFilter`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md#protobuf) with a single `contentTopic`.
+- Add `timestamp` field to [`WakuMessage`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/14/message.md#payloads).
+- Ensure CLI config parameters use a consistent naming scheme. Summary of changes [here](https://github.com/status-im/nim-waku/pull/543).
+
+#### Docs
+
+Several clarifications and additions aimed at contributors, including
+ - information on [how to query Status test fleet](https://github.com/status-im/nim-waku/blob/master/docs/faq.md) for node addresses,
+ - [how to view logs](https://github.com/status-im/nim-waku/blob/master/docs/contributors/cluster-logs.md), and
+ - [how to update submodules](https://github.com/status-im/nim-waku/blob/master/docs/contributors/git-submodules.md).
+
+#### Schema
+
+- Add `Message` table to the persistent message store. This table replaces the old `messages` table. It has two additional columns, namely
+ - `pubsubTopic`, and
+ - `version`.
+- Add `Peer` table for persistent peer storage.
+
+#### API
+
+- [JSON-RPC Admin API](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/16/rpc.md): Added a [`post` method](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/16/rpc.md#post_waku_v2_admin_v1_peers) to connect to peers on an ad-hoc basis.
+- [Nim API](https://github.com/status-im/nim-waku/blob/master/docs/api/v2/node.md): PubSub topic `subscribe` and `unsubscribe` no longer returns a future (removed `async` designation).
+- [`HistoryQuery`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md#historyquery): Added `pubsubTopic` field. Message history can now be filtered and queried based on the `pubsubTopic`.
+- [`HistoryQuery`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md#historyquery): Added support for querying a time window by specifying start and end times.
+
+### Fixes
+
+- Running nodes can now be shut down gracefully
+- Content filtering now works on any PubSub topic and not just the `waku` default.
+- Nodes can now mount protocols without supporting `relay` as a capability
+
+The [Waku v2 suite of protocols](https://github.com/waku-org/specs) are still in a raw/draft state.
+This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
+| Protocol | Spec status | Protocol id |
+| ---: | :---: | :--- |
+| [`17/WAKU-RLN`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/17/rln-relay.md) | `raw` | `/vac/waku/waku-rln-relay/2.0.0-alpha1` |
+| [`18/WAKU2-SWAP`](https://github.com/vacp2p/rfc-index/blob/main/waku/deprecated/18/swap.md) | `raw` | `/vac/waku/swap/2.0.0-alpha1` |
+| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `raw` | `/vac/waku/lightpush/2.0.0-alpha1` |
+| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `draft` | `/vac/waku/relay/2.0.0-beta2` |
+| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` |
+| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta3` |
+
+The Waku v1 implementation is stable but not under active development.
+
+## 2021-01-05 v0.2
+
+This release contains the following changes:
+
+- Calls to `publish` a message on `wakunode2` now `await` instead of `discard` dispatched [`WakuRelay`](https://github.com/vacp2p/specs/blob/master/specs/waku/v2/waku-relay.md) procedures.
+- [`StrictNoSign`](https://github.com/libp2p/specs/tree/master/pubsub#message-signing) enabled.
+- Add JSON-RPC API for external access to `wakunode2` functionality:
+ - Admin API retrieves information about peers registered on the `wakunode2`.
+ - Debug API exposes debug information about a `wakunode2`.
+ - Filter API saves bandwidth by allowing light nodes to filter for specific content.
+ - Private API enables symmetric or asymmetric cryptography to encrypt/decrypt message payloads.
+ - Relay API allows basic pub/sub functionality.
+ - Store API retrieves historical messages.
+- Add tutorial on how to use JSON-RPC API.
+- Refactor: Move `waku_filter` protocol into its own module.
+
+The Waku v2 implementation, and [most protocols it consist of](https://specs.vac.dev/specs/waku/),
+are still in a draft/beta state. The Waku v1 implementation is stable but not under active development.
+
+## 2020-11-30 v0.1
+
+Initial beta release.
+
+This release contains:
+
+- A Nim implementation of the [Waku v1 protocol](https://specs.vac.dev/waku/waku.html).
+- A Nim implementation of the [Waku v2 protocol](https://specs.vac.dev/specs/waku/v2/waku-v2.html).
+- CLI applications `wakunode` and `wakunode2` that allows you to run a Waku v1 or v2 node.
+- Examples of Waku v1 and v2 usage.
+- Various tests of above.
+
+Currenty the Waku v2 implementation, and [most protocols it consist of](https://specs.vac.dev/specs/waku/),
+are in a draft/beta state. The Waku v1 implementation is stable but not under active development.
+
+Feedback welcome!
diff --git a/third-party/nwaku/Dockerfile b/third-party/nwaku/Dockerfile
new file mode 100644
index 0000000..6afb2bc
--- /dev/null
+++ b/third-party/nwaku/Dockerfile
@@ -0,0 +1,93 @@
+# BUILD NIM APP ----------------------------------------------------------------
+FROM rust:1.81.0-alpine3.19 AS nim-build
+
+ARG NIMFLAGS
+ARG MAKE_TARGET=wakunode2
+ARG NIM_COMMIT
+ARG LOG_LEVEL=TRACE
+ARG HEAPTRACK_BUILD=0
+
+# Get build tools and required header files
+RUN apk add --no-cache bash git build-base openssl-dev linux-headers curl jq
+
+WORKDIR /app
+COPY . .
+
+# workaround for alpine issue: https://github.com/alpinelinux/docker-alpine/issues/383
+RUN apk update && apk upgrade
+
+# Ran separately from 'make' to avoid re-doing
+RUN git submodule update --init --recursive
+
+RUN if [ "$HEAPTRACK_BUILD" = "1" ]; then \
+ git apply --directory=vendor/nimbus-build-system/vendor/Nim docs/tutorial/nim.2.2.4_heaptracker_addon.patch; \
+ fi
+
+# Slowest build step for the sake of caching layers
+RUN make -j$(nproc) deps QUICK_AND_DIRTY_COMPILER=1 ${NIM_COMMIT}
+
+# Build the final node binary
+RUN make -j$(nproc) ${NIM_COMMIT} $MAKE_TARGET LOG_LEVEL=${LOG_LEVEL} NIMFLAGS="${NIMFLAGS}"
+
+
+# PRODUCTION IMAGE -------------------------------------------------------------
+
+FROM alpine:3.18 AS prod
+
+ARG MAKE_TARGET=wakunode2
+
+LABEL maintainer="jakub@status.im"
+LABEL source="https://github.com/waku-org/nwaku"
+LABEL description="Wakunode: Waku client"
+LABEL commit="unknown"
+LABEL version="unknown"
+
+# DevP2P, LibP2P, and JSON RPC ports
+EXPOSE 30303 60000 8545
+
+# Referenced in the binary
+RUN apk add --no-cache libgcc libpq-dev bind-tools
+
+# Copy to separate location to accomodate different MAKE_TARGET values
+COPY --from=nim-build /app/build/$MAKE_TARGET /usr/local/bin/
+
+# Copy migration scripts for DB upgrades
+COPY --from=nim-build /app/migrations/ /app/migrations/
+
+# Symlink the correct wakunode binary
+RUN ln -sv /usr/local/bin/$MAKE_TARGET /usr/bin/wakunode
+
+ENTRYPOINT ["/usr/bin/wakunode"]
+
+# By default just show help if called without arguments
+CMD ["--help"]
+
+
+# DEBUG IMAGE ------------------------------------------------------------------
+
+# Build debug tools: heaptrack
+FROM alpine:3.18 AS heaptrack-build
+
+RUN apk update
+RUN apk add -- gdb git g++ make cmake zlib-dev boost-dev libunwind-dev
+RUN git clone https://github.com/KDE/heaptrack.git /heaptrack
+
+WORKDIR /heaptrack/build
+# going to a commit that builds properly. We will revisit this for new releases
+RUN git reset --hard f9cc35ebbdde92a292fe3870fe011ad2874da0ca
+RUN cmake -DCMAKE_BUILD_TYPE=Release ..
+RUN make -j$(nproc)
+
+
+# Debug image
+FROM prod AS debug-with-heaptrack
+
+RUN apk add --no-cache gdb libunwind
+
+# Add heaptrack
+COPY --from=heaptrack-build /heaptrack/build/ /heaptrack/build/
+
+ENV LD_LIBRARY_PATH=/heaptrack/build/lib/heaptrack/
+RUN ln -s /heaptrack/build/bin/heaptrack /usr/local/bin/heaptrack
+
+ENTRYPOINT ["/heaptrack/build/bin/heaptrack", "/usr/bin/wakunode"]
diff --git a/third-party/nwaku/Dockerfile.lightpushWithMix.compile b/third-party/nwaku/Dockerfile.lightpushWithMix.compile
new file mode 100644
index 0000000..e39b88d
--- /dev/null
+++ b/third-party/nwaku/Dockerfile.lightpushWithMix.compile
@@ -0,0 +1,58 @@
+# BUILD NIM APP ----------------------------------------------------------------
+FROM rust:1.81.0-alpine3.19 AS nim-build
+
+ARG NIMFLAGS
+ARG MAKE_TARGET=lightpushwithmix
+ARG NIM_COMMIT
+ARG LOG_LEVEL=TRACE
+
+# Get build tools and required header files
+RUN apk add --no-cache bash git build-base openssl-dev pcre-dev linux-headers curl jq
+
+WORKDIR /app
+COPY . .
+
+# workaround for alpine issue: https://github.com/alpinelinux/docker-alpine/issues/383
+RUN apk update && apk upgrade
+
+# Ran separately from 'make' to avoid re-doing
+RUN git submodule update --init --recursive
+
+# Slowest build step for the sake of caching layers
+RUN make -j$(nproc) deps QUICK_AND_DIRTY_COMPILER=1 ${NIM_COMMIT}
+
+# Build the final node binary
+RUN make -j$(nproc) ${NIM_COMMIT} $MAKE_TARGET LOG_LEVEL=${LOG_LEVEL} NIMFLAGS="${NIMFLAGS}"
+
+
+# REFERENCE IMAGE as BASE for specialized PRODUCTION IMAGES----------------------------------------
+FROM alpine:3.18 AS base_lpt
+
+ARG MAKE_TARGET=lightpushwithmix
+
+LABEL maintainer="prem@waku.org"
+LABEL source="https://github.com/waku-org/nwaku"
+LABEL description="Lite Push With Mix: Waku light-client"
+LABEL commit="unknown"
+LABEL version="unknown"
+
+# DevP2P, LibP2P, and JSON RPC ports
+EXPOSE 30303 60000 8545
+
+# Referenced in the binary
+RUN apk add --no-cache libgcc pcre-dev libpq-dev \
+ wget \
+ iproute2 \
+ python3 \
+ jq
+
+# Fix for 'Error loading shared library libpcre.so.3: No such file or directory'
+RUN ln -s /usr/lib/libpcre.so /usr/lib/libpcre.so.3
+
+COPY --from=nim-build /app/build/lightpush_publisher_mix /usr/bin/
+RUN chmod +x /usr/bin/lightpush_publisher_mix
+
+# Standalone image to be used manually and in lpt-runner -------------------------------------------
+FROM base_lpt AS standalone_lpt
+
+ENTRYPOINT ["/usr/bin/lightpush_publisher_mix"]
diff --git a/third-party/nwaku/LICENSE-APACHEv2 b/third-party/nwaku/LICENSE-APACHEv2
new file mode 100644
index 0000000..7b6a3cb
--- /dev/null
+++ b/third-party/nwaku/LICENSE-APACHEv2
@@ -0,0 +1,205 @@
+nim-waku is licensed under the Apache License version 2
+Copyright (c) 2018 Status Research & Development GmbH
+-----------------------------------------------------
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2018 Status Research & Development GmbH
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/third-party/nwaku/LICENSE-MIT b/third-party/nwaku/LICENSE-MIT
new file mode 100644
index 0000000..aab8020
--- /dev/null
+++ b/third-party/nwaku/LICENSE-MIT
@@ -0,0 +1,25 @@
+nim-waku is licensed under the MIT License
+Copyright (c) 2018 Status Research & Development GmbH
+-----------------------------------------------------
+
+The MIT License (MIT)
+
+Copyright (c) 2018 Status Research & Development GmbH
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/third-party/nwaku/Makefile b/third-party/nwaku/Makefile
new file mode 100644
index 0000000..d5cf088
--- /dev/null
+++ b/third-party/nwaku/Makefile
@@ -0,0 +1,546 @@
+# Copyright (c) 2022 Status Research & Development GmbH. Licensed under
+# either of:
+# - Apache License, version 2.0
+# - MIT license
+# at your option. This file may not be copied, modified, or distributed except
+# according to those terms.
+export BUILD_SYSTEM_DIR := vendor/nimbus-build-system
+export EXCLUDED_NIM_PACKAGES := vendor/nim-dnsdisc/vendor
+LINK_PCRE := 0
+FORMAT_MSG := "\\x1B[95mFormatting:\\x1B[39m"
+# we don't want an error here, so we can handle things later, in the ".DEFAULT" target
+-include $(BUILD_SYSTEM_DIR)/makefiles/variables.mk
+
+
+ifeq ($(NIM_PARAMS),)
+# "variables.mk" was not included, so we update the submodules.
+GIT_SUBMODULE_UPDATE := git submodule update --init --recursive
+.DEFAULT:
+ +@ echo -e "Git submodules not found. Running '$(GIT_SUBMODULE_UPDATE)'.\n"; \
+ $(GIT_SUBMODULE_UPDATE); \
+ echo
+# Now that the included *.mk files appeared, and are newer than this file, Make will restart itself:
+# https://www.gnu.org/software/make/manual/make.html#Remaking-Makefiles
+#
+# After restarting, it will execute its original goal, so we don't have to start a child Make here
+# with "$(MAKE) $(MAKECMDGOALS)". Isn't hidden control flow great?
+
+else # "variables.mk" was included. Business as usual until the end of this file.
+
+# Determine the OS
+detected_OS := $(shell uname -s)
+ifneq (,$(findstring MINGW,$(detected_OS)))
+ detected_OS := Windows
+endif
+
+ifeq ($(detected_OS),Windows)
+ # Update MINGW_PATH to standard MinGW location
+ MINGW_PATH = /mingw64
+ NIM_PARAMS += --passC:"-I$(MINGW_PATH)/include"
+ NIM_PARAMS += --passL:"-L$(MINGW_PATH)/lib"
+ NIM_PARAMS += --passL:"-Lvendor/nim-nat-traversal/vendor/miniupnp/miniupnpc"
+ NIM_PARAMS += --passL:"-Lvendor/nim-nat-traversal/vendor/libnatpmp-upstream"
+
+ LIBS = -lws2_32 -lbcrypt -liphlpapi -luserenv -lntdll -lminiupnpc -lnatpmp -lpq
+ NIM_PARAMS += $(foreach lib,$(LIBS),--passL:"$(lib)")
+endif
+
+##########
+## Main ##
+##########
+.PHONY: all test update clean
+
+# default target, because it's the first one that doesn't start with '.'
+all: | wakunode2 example2 chat2 chat2bridge libwaku
+
+test_file := $(word 2,$(MAKECMDGOALS))
+define test_name
+$(shell echo '$(MAKECMDGOALS)' | cut -d' ' -f3-)
+endef
+
+test:
+ifeq ($(strip $(test_file)),)
+ $(MAKE) testcommon
+ $(MAKE) testwaku
+else
+ $(MAKE) compile-test TEST_FILE="$(test_file)" TEST_NAME="$(call test_name)"
+endif
+# this prevents make from erroring on unknown targets like "Index"
+%:
+ @true
+
+waku.nims:
+ ln -s waku.nimble $@
+
+update: | update-common
+ rm -rf waku.nims && \
+ $(MAKE) waku.nims $(HANDLE_OUTPUT)
+ $(MAKE) build-nph
+
+clean:
+ rm -rf build
+
+# must be included after the default target
+-include $(BUILD_SYSTEM_DIR)/makefiles/targets.mk
+
+## Possible values: prod; debug
+TARGET ?= prod
+
+## Git version
+GIT_VERSION ?= $(shell git describe --abbrev=6 --always --tags)
+## Compilation parameters. If defined in the CLI the assignments won't be executed
+NIM_PARAMS := $(NIM_PARAMS) -d:git_version=\"$(GIT_VERSION)\"
+
+## Heaptracker options
+HEAPTRACKER ?= 0
+HEAPTRACKER_INJECT ?= 0
+ifeq ($(HEAPTRACKER), 1)
+# Assumes Nim's lib/system/alloc.nim is patched!
+TARGET := debug-with-heaptrack
+
+ifeq ($(HEAPTRACKER_INJECT), 1)
+# the Nim compiler will load 'libheaptrack_inject.so'
+HEAPTRACK_PARAMS := -d:heaptracker -d:heaptracker_inject
+NIM_PARAMS := $(NIM_PARAMS) -d:heaptracker -d:heaptracker_inject
+else
+# the Nim compiler will load 'libheaptrack_preload.so'
+HEAPTRACK_PARAMS := -d:heaptracker
+NIM_PARAMS := $(NIM_PARAMS) -d:heaptracker
+endif
+
+endif
+## end of Heaptracker options
+
+##################
+## Dependencies ##
+##################
+.PHONY: deps libbacktrace
+
+rustup:
+ifeq (, $(shell which cargo))
+# Install Rustup if it's not installed
+# -y: Assume "yes" for all prompts
+# --default-toolchain stable: Install the stable toolchain
+ curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable
+endif
+
+rln-deps: rustup
+ ./scripts/install_rln_tests_dependencies.sh
+
+deps: | deps-common nat-libs waku.nims
+
+
+### nim-libbacktrace
+
+# "-d:release" implies "--stacktrace:off" and it cannot be added to config.nims
+ifeq ($(DEBUG), 0)
+NIM_PARAMS := $(NIM_PARAMS) -d:release
+else
+NIM_PARAMS := $(NIM_PARAMS) -d:debug
+endif
+
+ifeq ($(USE_LIBBACKTRACE), 0)
+NIM_PARAMS := $(NIM_PARAMS) -d:disable_libbacktrace
+endif
+
+libbacktrace:
+ + $(MAKE) -C vendor/nim-libbacktrace --no-print-directory BUILD_CXX_LIB=0
+
+clean-libbacktrace:
+ + $(MAKE) -C vendor/nim-libbacktrace clean $(HANDLE_OUTPUT)
+
+# Extend deps and clean targets
+ifneq ($(USE_LIBBACKTRACE), 0)
+deps: | libbacktrace
+endif
+
+ifeq ($(POSTGRES), 1)
+NIM_PARAMS := $(NIM_PARAMS) -d:postgres -d:nimDebugDlOpen
+endif
+
+ifeq ($(DEBUG_DISCV5), 1)
+NIM_PARAMS := $(NIM_PARAMS) -d:debugDiscv5
+endif
+
+clean: | clean-libbacktrace
+
+### Create nimble links (used when building with Nix)
+
+nimbus-build-system-nimble-dir:
+ NIMBLE_DIR="$(CURDIR)/$(NIMBLE_DIR)" \
+ PWD_CMD="$(PWD)" \
+ $(CURDIR)/scripts/generate_nimble_links.sh
+
+##################
+## RLN ##
+##################
+.PHONY: librln
+
+LIBRLN_BUILDDIR := $(CURDIR)/vendor/zerokit
+LIBRLN_VERSION := v0.7.0
+
+ifeq ($(detected_OS),Windows)
+LIBRLN_FILE := rln.lib
+else
+LIBRLN_FILE := librln_$(LIBRLN_VERSION).a
+endif
+
+$(LIBRLN_FILE):
+ echo -e $(BUILD_MSG) "$@" && \
+ ./scripts/build_rln.sh $(LIBRLN_BUILDDIR) $(LIBRLN_VERSION) $(LIBRLN_FILE)
+
+librln: | $(LIBRLN_FILE)
+ $(eval NIM_PARAMS += --passL:$(LIBRLN_FILE) --passL:-lm)
+
+clean-librln:
+ cargo clean --manifest-path vendor/zerokit/rln/Cargo.toml
+ rm -f $(LIBRLN_FILE)
+
+# Extend clean target
+clean: | clean-librln
+
+#################
+## Waku Common ##
+#################
+.PHONY: testcommon
+
+testcommon: | build deps
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim testcommon $(NIM_PARAMS) waku.nims
+
+
+##########
+## Waku ##
+##########
+.PHONY: testwaku wakunode2 testwakunode2 example2 chat2 chat2bridge liteprotocoltester
+
+# install rln-deps only for the testwaku target
+testwaku: | build deps rln-deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim test -d:os=$(shell uname) $(NIM_PARAMS) waku.nims
+
+wakunode2: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ \
+ $(ENV_SCRIPT) nim wakunode2 $(NIM_PARAMS) waku.nims
+
+benchmarks: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim benchmarks $(NIM_PARAMS) waku.nims
+
+testwakunode2: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim testwakunode2 $(NIM_PARAMS) waku.nims
+
+example2: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim example2 $(NIM_PARAMS) waku.nims
+
+chat2: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim chat2 $(NIM_PARAMS) waku.nims
+
+chat2mix: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim chat2mix $(NIM_PARAMS) waku.nims
+
+rln-db-inspector: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim rln_db_inspector $(NIM_PARAMS) waku.nims
+
+chat2bridge: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim chat2bridge $(NIM_PARAMS) waku.nims
+
+liteprotocoltester: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim liteprotocoltester $(NIM_PARAMS) waku.nims
+
+lightpushwithmix: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim lightpushwithmix $(NIM_PARAMS) waku.nims
+
+build/%: | build deps librln
+ echo -e $(BUILD_MSG) "build/$*" && \
+ $(ENV_SCRIPT) nim buildone $(NIM_PARAMS) waku.nims $*
+
+compile-test: | build deps librln
+ echo -e $(BUILD_MSG) "$(TEST_FILE)" "\"$(TEST_NAME)\"" && \
+ $(ENV_SCRIPT) nim buildTest $(NIM_PARAMS) waku.nims $(TEST_FILE) && \
+ $(ENV_SCRIPT) nim execTest $(NIM_PARAMS) waku.nims $(TEST_FILE) "\"$(TEST_NAME)\""; \
+
+################
+## Waku tools ##
+################
+.PHONY: tools wakucanary networkmonitor
+
+tools: networkmonitor wakucanary
+
+wakucanary: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim wakucanary $(NIM_PARAMS) waku.nims
+
+networkmonitor: | build deps librln
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim networkmonitor $(NIM_PARAMS) waku.nims
+
+############
+## Format ##
+############
+.PHONY: build-nph install-nph clean-nph print-nph-path
+
+# Default location for nph binary shall be next to nim binary to make it available on the path.
+NPH:=$(shell dirname $(NIM_BINARY))/nph
+
+build-nph: | build deps
+ifeq ("$(wildcard $(NPH))","")
+ $(ENV_SCRIPT) nim c --skipParentCfg:on vendor/nph/src/nph.nim && \
+ mv vendor/nph/src/nph $(shell dirname $(NPH))
+ echo "nph utility is available at " $(NPH)
+else
+ echo "nph utility already exists at " $(NPH)
+endif
+
+GIT_PRE_COMMIT_HOOK := .git/hooks/pre-commit
+
+install-nph: build-nph
+ifeq ("$(wildcard $(GIT_PRE_COMMIT_HOOK))","")
+ cp ./scripts/git_pre_commit_format.sh $(GIT_PRE_COMMIT_HOOK)
+else
+ echo "$(GIT_PRE_COMMIT_HOOK) already present, will NOT override"
+ exit 1
+endif
+
+nph/%: | build-nph
+ echo -e $(FORMAT_MSG) "nph/$*" && \
+ $(NPH) $*
+
+clean-nph:
+ rm -f $(NPH)
+
+# To avoid hardcoding nph binary location in several places
+print-nph-path:
+ echo "$(NPH)"
+
+clean: | clean-nph
+
+###################
+## Documentation ##
+###################
+.PHONY: docs coverage
+
+# TODO: Remove unused target
+docs: | build deps
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) nim doc --run --index:on --project --out:.gh-pages waku/waku.nim waku.nims
+
+coverage:
+ echo -e $(BUILD_MSG) "build/$@" && \
+ $(ENV_SCRIPT) ./scripts/run_cov.sh -y
+
+
+#####################
+## Container image ##
+#####################
+# -d:insecure - Necessary to enable Prometheus HTTP endpoint for metrics
+# -d:chronicles_colors:none - Necessary to disable colors in logs for Docker
+DOCKER_IMAGE_NIMFLAGS ?= -d:chronicles_colors:none -d:insecure -d:postgres
+DOCKER_IMAGE_NIMFLAGS := $(DOCKER_IMAGE_NIMFLAGS) $(HEAPTRACK_PARAMS)
+
+# build a docker image for the fleet
+docker-image: MAKE_TARGET ?= wakunode2
+docker-image: DOCKER_IMAGE_TAG ?= $(MAKE_TARGET)-$(GIT_VERSION)
+docker-image: DOCKER_IMAGE_NAME ?= wakuorg/nwaku:$(DOCKER_IMAGE_TAG)
+docker-image:
+ docker build \
+ --build-arg="MAKE_TARGET=$(MAKE_TARGET)" \
+ --build-arg="NIMFLAGS=$(DOCKER_IMAGE_NIMFLAGS)" \
+ --build-arg="NIM_COMMIT=$(DOCKER_NIM_COMMIT)" \
+ --build-arg="LOG_LEVEL=$(LOG_LEVEL)" \
+ --build-arg="HEAPTRACK_BUILD=$(HEAPTRACKER)" \
+ --label="commit=$(shell git rev-parse HEAD)" \
+ --label="version=$(GIT_VERSION)" \
+ --target $(TARGET) \
+ --tag $(DOCKER_IMAGE_NAME) .
+
+docker-quick-image: MAKE_TARGET ?= wakunode2
+docker-quick-image: DOCKER_IMAGE_TAG ?= $(MAKE_TARGET)-$(GIT_VERSION)
+docker-quick-image: DOCKER_IMAGE_NAME ?= wakuorg/nwaku:$(DOCKER_IMAGE_TAG)
+docker-quick-image: NIM_PARAMS := $(NIM_PARAMS) -d:chronicles_colors:none -d:insecure -d:postgres --passL:$(LIBRLN_FILE) --passL:-lm
+docker-quick-image: | build deps librln wakunode2
+ docker build \
+ --build-arg="MAKE_TARGET=$(MAKE_TARGET)" \
+ --tag $(DOCKER_IMAGE_NAME) \
+ --target $(TARGET) \
+ --file docker/binaries/Dockerfile.bn.local \
+ .
+
+docker-push:
+ docker push $(DOCKER_IMAGE_NAME)
+
+####################################
+## Container lite-protocol-tester ##
+####################################
+# -d:insecure - Necessary to enable Prometheus HTTP endpoint for metrics
+# -d:chronicles_colors:none - Necessary to disable colors in logs for Docker
+DOCKER_LPT_NIMFLAGS ?= -d:chronicles_colors:none -d:insecure
+
+# build a docker image for the fleet
+docker-liteprotocoltester: DOCKER_LPT_TAG ?= latest
+docker-liteprotocoltester: DOCKER_LPT_NAME ?= wakuorg/liteprotocoltester:$(DOCKER_LPT_TAG)
+# --no-cache
+docker-liteprotocoltester:
+ docker build \
+ --build-arg="MAKE_TARGET=liteprotocoltester" \
+ --build-arg="NIMFLAGS=$(DOCKER_LPT_NIMFLAGS)" \
+ --build-arg="NIM_COMMIT=$(DOCKER_NIM_COMMIT)" \
+ --build-arg="LOG_LEVEL=TRACE" \
+ --label="commit=$(shell git rev-parse HEAD)" \
+ --label="version=$(GIT_VERSION)" \
+ --target $(if $(filter deploy,$(DOCKER_LPT_TAG)),deployment_lpt,standalone_lpt) \
+ --tag $(DOCKER_LPT_NAME) \
+ --file apps/liteprotocoltester/Dockerfile.liteprotocoltester.compile \
+ .
+
+docker-quick-liteprotocoltester: DOCKER_LPT_TAG ?= latest
+docker-quick-liteprotocoltester: DOCKER_LPT_NAME ?= wakuorg/liteprotocoltester:$(DOCKER_LPT_TAG)
+docker-quick-liteprotocoltester: | liteprotocoltester
+ docker build \
+ --tag $(DOCKER_LPT_NAME) \
+ --file apps/liteprotocoltester/Dockerfile.liteprotocoltester \
+ .
+
+docker-liteprotocoltester-push:
+ docker push $(DOCKER_LPT_NAME)
+
+
+################
+## C Bindings ##
+################
+.PHONY: cbindings cwaku_example libwaku
+
+STATIC ?= 0
+
+
+libwaku: | build deps librln
+ rm -f build/libwaku*
+
+ifeq ($(STATIC), 1)
+ echo -e $(BUILD_MSG) "build/$@.a" && $(ENV_SCRIPT) nim libwakuStatic $(NIM_PARAMS) waku.nims
+else ifeq ($(detected_OS),Windows)
+ echo -e $(BUILD_MSG) "build/$@.dll" && $(ENV_SCRIPT) nim libwakuDynamic $(NIM_PARAMS) waku.nims
+else
+ echo -e $(BUILD_MSG) "build/$@.so" && $(ENV_SCRIPT) nim libwakuDynamic $(NIM_PARAMS) waku.nims
+endif
+
+#####################
+## Mobile Bindings ##
+#####################
+.PHONY: libwaku-android \
+ libwaku-android-precheck \
+ libwaku-android-arm64 \
+ libwaku-android-amd64 \
+ libwaku-android-x86 \
+ libwaku-android-arm \
+ rebuild-nat-libs \
+ build-libwaku-for-android-arch
+
+ANDROID_TARGET ?= 30
+ifeq ($(detected_OS),Darwin)
+ ANDROID_TOOLCHAIN_DIR := $(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/darwin-x86_64
+else
+ ANDROID_TOOLCHAIN_DIR := $(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64
+endif
+
+rebuild-nat-libs: | clean-cross nat-libs
+
+libwaku-android-precheck:
+ifndef ANDROID_NDK_HOME
+ $(error ANDROID_NDK_HOME is not set)
+endif
+
+build-libwaku-for-android-arch:
+ $(MAKE) rebuild-nat-libs CC=$(ANDROID_TOOLCHAIN_DIR)/bin/$(ANDROID_COMPILER) && \
+ ./scripts/build_rln_android.sh $(CURDIR)/build $(LIBRLN_BUILDDIR) $(LIBRLN_VERSION) $(CROSS_TARGET) $(ABIDIR) && \
+ CPU=$(CPU) ABIDIR=$(ABIDIR) ANDROID_ARCH=$(ANDROID_ARCH) ANDROID_COMPILER=$(ANDROID_COMPILER) ANDROID_TOOLCHAIN_DIR=$(ANDROID_TOOLCHAIN_DIR) $(ENV_SCRIPT) nim libWakuAndroid $(NIM_PARAMS) waku.nims
+
+libwaku-android-arm64: ANDROID_ARCH=aarch64-linux-android
+libwaku-android-arm64: CPU=arm64
+libwaku-android-arm64: ABIDIR=arm64-v8a
+libwaku-android-arm64: | libwaku-android-precheck build deps
+ $(MAKE) build-libwaku-for-android-arch ANDROID_ARCH=$(ANDROID_ARCH) CROSS_TARGET=$(ANDROID_ARCH) CPU=$(CPU) ABIDIR=$(ABIDIR) ANDROID_COMPILER=$(ANDROID_ARCH)$(ANDROID_TARGET)-clang
+
+libwaku-android-amd64: ANDROID_ARCH=x86_64-linux-android
+libwaku-android-amd64: CPU=amd64
+libwaku-android-amd64: ABIDIR=x86_64
+libwaku-android-amd64: | libwaku-android-precheck build deps
+ $(MAKE) build-libwaku-for-android-arch ANDROID_ARCH=$(ANDROID_ARCH) CROSS_TARGET=$(ANDROID_ARCH) CPU=$(CPU) ABIDIR=$(ABIDIR) ANDROID_COMPILER=$(ANDROID_ARCH)$(ANDROID_TARGET)-clang
+
+libwaku-android-x86: ANDROID_ARCH=i686-linux-android
+libwaku-android-x86: CPU=i386
+libwaku-android-x86: ABIDIR=x86
+libwaku-android-x86: | libwaku-android-precheck build deps
+ $(MAKE) build-libwaku-for-android-arch ANDROID_ARCH=$(ANDROID_ARCH) CROSS_TARGET=$(ANDROID_ARCH) CPU=$(CPU) ABIDIR=$(ABIDIR) ANDROID_COMPILER=$(ANDROID_ARCH)$(ANDROID_TARGET)-clang
+
+libwaku-android-arm: ANDROID_ARCH=armv7a-linux-androideabi
+libwaku-android-arm: CPU=arm
+libwaku-android-arm: ABIDIR=armeabi-v7a
+libwaku-android-arm: | libwaku-android-precheck build deps
+# cross-rs target architecture name does not match the one used in android
+ $(MAKE) build-libwaku-for-android-arch ANDROID_ARCH=$(ANDROID_ARCH) CROSS_TARGET=armv7-linux-androideabi CPU=$(CPU) ABIDIR=$(ABIDIR) ANDROID_COMPILER=$(ANDROID_ARCH)$(ANDROID_TARGET)-clang
+
+libwaku-android:
+ $(MAKE) libwaku-android-amd64
+ $(MAKE) libwaku-android-arm64
+ $(MAKE) libwaku-android-x86
+# This target is disabled because on recent versions of cross-rs complain with the following error
+# relocation R_ARM_THM_ALU_PREL_11_0 cannot be used against symbol 'stack_init_trampoline_return'; recompile with -fPIC
+# It's likely this architecture is not used so we might just not support it.
+# $(MAKE) libwaku-android-arm
+
+cwaku_example: | build libwaku
+ echo -e $(BUILD_MSG) "build/$@" && \
+ cc -o "build/$@" \
+ ./examples/cbindings/waku_example.c \
+ ./examples/cbindings/base64.c \
+ -lwaku -Lbuild/ \
+ -pthread -ldl -lm \
+ -lminiupnpc -Lvendor/nim-nat-traversal/vendor/miniupnp/miniupnpc/build/ \
+ -lnatpmp -Lvendor/nim-nat-traversal/vendor/libnatpmp-upstream/ \
+ vendor/nim-libbacktrace/libbacktrace_wrapper.o \
+ vendor/nim-libbacktrace/install/usr/lib/libbacktrace.a
+
+cppwaku_example: | build libwaku
+ echo -e $(BUILD_MSG) "build/$@" && \
+ g++ -o "build/$@" \
+ ./examples/cpp/waku.cpp \
+ ./examples/cpp/base64.cpp \
+ -lwaku -Lbuild/ \
+ -pthread -ldl -lm \
+ -lminiupnpc -Lvendor/nim-nat-traversal/vendor/miniupnp/miniupnpc/build/ \
+ -lnatpmp -Lvendor/nim-nat-traversal/vendor/libnatpmp-upstream/ \
+ vendor/nim-libbacktrace/libbacktrace_wrapper.o \
+ vendor/nim-libbacktrace/install/usr/lib/libbacktrace.a
+
+nodejswaku: | build deps
+ echo -e $(BUILD_MSG) "build/$@" && \
+ node-gyp build --directory=examples/nodejs/
+
+endif # "variables.mk" was not included
+
+###################
+# Release Targets #
+###################
+
+release-notes:
+ docker run \
+ -it \
+ --rm \
+ -v $${PWD}:/opt/sv4git/repo:z \
+ -u $(shell id -u) \
+ docker.io/wakuorg/sv4git:latest \
+ release-notes |\
+ sed -E 's@#([0-9]+)@[#\1](https://github.com/waku-org/nwaku/issues/\1)@g'
+# I could not get the tool to replace issue ids with links, so using sed for now,
+# asked here: https://github.com/bvieira/sv4git/discussions/101
+
diff --git a/third-party/nwaku/README.md b/third-party/nwaku/README.md
new file mode 100644
index 0000000..ce352d6
--- /dev/null
+++ b/third-party/nwaku/README.md
@@ -0,0 +1,186 @@
+# Nwaku
+
+## Introduction
+
+The nwaku repository implements Waku, and provides tools related to it.
+
+- A Nim implementation of the [Waku (v2) protocol](https://specs.vac.dev/specs/waku/v2/waku-v2.html).
+- CLI application `wakunode2` that allows you to run a Waku node.
+- Examples of Waku usage.
+- Various tests of above.
+
+For more details see the [source code](waku/README.md)
+
+## How to Build & Run ( Linux, MacOS & WSL )
+
+These instructions are generic. For more detailed instructions, see the Waku source code above.
+
+### Prerequisites
+
+The standard developer tools, including a C compiler, GNU Make, Bash, and Git. More information on these installations can be found [here](https://docs.waku.org/guides/nwaku/build-source#install-dependencies).
+
+> In some distributions (Fedora linux for example), you may need to install `which` utility separately. Nimbus build system is relying on it.
+
+You'll also need an installation of Rust and its toolchain (specifically `rustc` and `cargo`).
+The easiest way to install these, is using `rustup`:
+
+```bash
+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
+```
+
+### Wakunode
+
+```bash
+# The first `make` invocation will update all Git submodules.
+# You'll run `make update` after each `git pull` in the future to keep those submodules updated.
+make wakunode2
+
+# Build with custom compilation flags. Do not use NIM_PARAMS unless you know what you are doing.
+# Replace with your own flags
+make wakunode2 NIMFLAGS="-d:chronicles_colors:none -d:disableMarchNative"
+
+# Run with DNS bootstrapping
+./build/wakunode2 --dns-discovery --dns-discovery-url=DNS_BOOTSTRAP_NODE_URL
+
+# See available command line options
+./build/wakunode2 --help
+```
+To join the network, you need to know the address of at least one bootstrap node.
+Please refer to the [Waku README](https://github.com/waku-org/nwaku/blob/master/waku/README.md) for more information.
+
+For more on how to run `wakunode2`, refer to:
+- [Run using binaries](https://docs.waku.org/guides/nwaku/build-source)
+- [Run using docker](https://docs.waku.org/guides/nwaku/run-docker)
+- [Run using docker-compose](https://docs.waku.org/guides/nwaku/run-docker-compose)
+
+#### Issues
+##### WSL
+If you encounter difficulties building the project on WSL, consider placing the project within WSL's filesystem, avoiding the `/mnt/` directory.
+
+### How to Build & Run ( Windows )
+
+### Windows Build Instructions
+
+#### 1. Install Required Tools
+- **Git Bash Terminal**: Download and install from https://git-scm.com/download/win
+- **MSYS2**:
+ a. Download installer from https://www.msys2.org
+ b. Install at "C:\" (default location). Remove/rename the msys folder in case of previous installation.
+ c. Use the mingw64 terminal from msys64 directory for package installation.
+
+#### 2. Install Dependencies
+Open MSYS2 mingw64 terminal and run the following one-by-one :
+```bash
+pacman -Syu --noconfirm
+pacman -S --noconfirm --needed mingw-w64-x86_64-toolchain
+pacman -S --noconfirm --needed base-devel make cmake upx
+pacman -S --noconfirm --needed mingw-w64-x86_64-rust
+pacman -S --noconfirm --needed mingw-w64-x86_64-postgresql
+pacman -S --noconfirm --needed mingw-w64-x86_64-gcc
+pacman -S --noconfirm --needed mingw-w64-x86_64-gcc-libs
+pacman -S --noconfirm --needed mingw-w64-x86_64-libwinpthread-git
+pacman -S --noconfirm --needed mingw-w64-x86_64-zlib
+pacman -S --noconfirm --needed mingw-w64-x86_64-openssl
+pacman -S --noconfirm --needed mingw-w64-x86_64-python
+```
+
+#### 3. Build Wakunode
+- Open Git Bash as administrator
+- clone nwaku and cd nwaku
+- Execute: `./scripts/build_windows.sh`
+
+#### 4. Troubleshooting
+If `wakunode2.exe` isn't generated:
+- **Missing Dependencies**: Verify with:
+ `which make cmake gcc g++ rustc cargo python3 upx`
+ If missing, revisit Step 2 or ensure MSYS2 is at `C:\`
+- **Installation Conflicts**: Remove existing MinGW/MSYS2/Git Bash installations and perform fresh install
+
+### Developing
+
+#### Nim Runtime
+This repository is bundled with a Nim runtime that includes the necessary dependencies for the project.
+
+Before you can utilize the runtime you'll need to build the project, as detailed in a previous section.
+This will generate a `vendor` directory containing various dependencies, including the `nimbus-build-system` which has the bundled nim runtime.
+
+After successfully building the project, you may bring the bundled runtime into scope by running:
+```bash
+source env.sh
+```
+If everything went well, you should see your prompt suffixed with `[Nimbus env]$`. Now you can run `nim` commands as usual.
+
+### Test Suite
+
+```bash
+# Run all the Waku tests
+make test
+
+# Run a specific test file
+make test
+# e.g. : make test tests/wakunode2/test_all.nim
+
+# Run a specific test name from a specific test file
+make test
+# e.g. : make test tests/wakunode2/test_all.nim "node setup is successful with default configuration"
+```
+
+### Building single test files
+
+During development it is helpful to build and run a single test file.
+To support this make has a specific target:
+
+targets:
+- `build/`
+- `test/`
+
+Binary will be created as `.bin` under the `build` directory .
+
+```bash
+# Build and run your test file separately
+make test/tests/common/test_enr_builder.nim
+```
+
+### Testing against `js-waku`
+Refer to [js-waku repo](https://github.com/waku-org/js-waku/tree/master/packages/tests) for instructions.
+
+## Formatting
+
+Nim files are expected to be formatted using the [`nph`](https://github.com/arnetheduck/nph) version present in `vendor/nph`.
+
+You can easily format file with the `make nph/ file` command.
+For example:
+
+```
+make nph/waku/waku_core.nim
+```
+
+A convenient git hook is provided to automatically format file at commit time.
+Run the following command to install it:
+
+```shell
+make install-nph
+```
+
+### Examples
+
+Examples can be found in the examples folder.
+This includes a fully featured chat example.
+
+### Tools
+
+Different tools and their corresponding how-to guides can be found in the `tools` folder.
+
+### Bugs, Questions & Features
+
+For an inquiry, or if you would like to propose new features, feel free to [open a general issue](https://github.com/waku-org/nwaku/issues/new).
+
+For bug reports, please [tag your issue with the `bug` label](https://github.com/waku-org/nwaku/issues/new).
+
+If you believe the reported issue requires critical attention, please [use the `critical` label](https://github.com/waku-org/nwaku/issues/new?labels=critical,bug) to assist with triaging.
+
+To get help, or participate in the conversation, join the [Waku Discord](https://discord.waku.org/) server.
+
+### Docs
+
+* [REST API Documentation](https://waku-org.github.io/waku-rest-api/)
diff --git a/third-party/nwaku/apps/benchmarks/benchmarks.nim b/third-party/nwaku/apps/benchmarks/benchmarks.nim
new file mode 100644
index 0000000..75686c8
--- /dev/null
+++ b/third-party/nwaku/apps/benchmarks/benchmarks.nim
@@ -0,0 +1,74 @@
+import
+ std/[strutils, times, sequtils, osproc], math, results, options, testutils/unittests
+
+import
+ waku/[
+ waku_rln_relay/protocol_types,
+ waku_rln_relay/rln,
+ waku_rln_relay,
+ waku_rln_relay/conversion_utils,
+ waku_rln_relay/group_manager/on_chain/group_manager,
+ ],
+ tests/waku_rln_relay/utils_onchain
+
+proc benchmark(
+ manager: OnChainGroupManager, registerCount: int, messageLimit: int
+): Future[string] {.async, gcsafe.} =
+ # Register a new member so that we can later generate proofs
+ let idCredentials = generateCredentials(manager.rlnInstance, registerCount)
+
+ var start_time = getTime()
+ for i in 0 .. registerCount - 1:
+ try:
+ await manager.register(idCredentials[i], UserMessageLimit(messageLimit + 1))
+ except Exception, CatchableError:
+ assert false, "exception raised: " & getCurrentExceptionMsg()
+
+ debug "registration finished",
+ iter = i, elapsed_ms = (getTime() - start_time).inMilliseconds
+
+ discard await manager.updateRoots()
+ let proofResult = await manager.fetchMerkleProofElements()
+ if proofResult.isErr():
+ error "Failed to fetch Merkle proof", error = proofResult.error
+ manager.merkleProofCache = proofResult.get()
+
+ let epoch = default(Epoch)
+ debug "epoch in bytes", epochHex = epoch.inHex()
+ let data: seq[byte] = newSeq[byte](1024)
+
+ var proofGenTimes: seq[times.Duration] = @[]
+ var proofVerTimes: seq[times.Duration] = @[]
+
+ start_time = getTime()
+ for i in 1 .. messageLimit:
+ var generate_time = getTime()
+ let proof = manager.generateProof(data, epoch, MessageId(i.uint8)).valueOr:
+ raiseAssert $error
+ proofGenTimes.add(getTime() - generate_time)
+
+ let verify_time = getTime()
+ let ok = manager.verifyProof(data, proof).valueOr:
+ raiseAssert $error
+ proofVerTimes.add(getTime() - verify_time)
+ debug "iteration finished",
+ iter = i, elapsed_ms = (getTime() - start_time).inMilliseconds
+
+ echo "Proof generation times: ", sum(proofGenTimes) div len(proofGenTimes)
+ echo "Proof verification times: ", sum(proofVerTimes) div len(proofVerTimes)
+
+proc main() =
+ # Start a local Ethereum JSON-RPC (Anvil) so that the group-manager setup can connect.
+ let anvilProc = runAnvil()
+ defer:
+ stopAnvil(anvilProc)
+
+ # Set up an On-chain group manager (includes contract deployment)
+ let manager = waitFor setupOnchainGroupManager()
+ (waitFor manager.init()).isOkOr:
+ raiseAssert $error
+
+ discard waitFor benchmark(manager, 200, 20)
+
+when isMainModule:
+ main()
diff --git a/third-party/nwaku/apps/chat2/chat2.nim b/third-party/nwaku/apps/chat2/chat2.nim
new file mode 100644
index 0000000..1531a46
--- /dev/null
+++ b/third-party/nwaku/apps/chat2/chat2.nim
@@ -0,0 +1,626 @@
+## chat2 is an example of usage of Waku v2. For suggested usage options, please
+## see dingpu tutorial in docs folder.
+
+when not (compileOption("threads")):
+ {.fatal: "Please, compile this program with the --threads:on option!".}
+
+{.push raises: [].}
+
+import std/[strformat, strutils, times, options, random, sequtils]
+import
+ confutils,
+ chronicles,
+ chronos,
+ eth/keys,
+ bearssl,
+ stew/[byteutils, results],
+ metrics,
+ metrics/chronos_httpserver
+import
+ libp2p/[
+ switch, # manage transports, a single entry point for dialing and listening
+ crypto/crypto, # cryptographic functions
+ stream/connection, # create and close stream read / write connections
+ multiaddress,
+ # encode different addressing schemes. For example, /ip4/7.7.7.7/tcp/6543 means it is using IPv4 protocol and TCP
+ peerinfo,
+ # manage the information of a peer, such as peer ID and public / private key
+ peerid, # Implement how peers interact
+ protobuf/minprotobuf, # message serialisation/deserialisation from and to protobufs
+ nameresolving/dnsresolver,
+ ] # define DNS resolution
+import
+ waku/[
+ waku_core,
+ waku_lightpush_legacy/common,
+ waku_lightpush_legacy/rpc,
+ waku_enr,
+ discovery/waku_dnsdisc,
+ waku_store_legacy,
+ waku_node,
+ node/waku_metrics,
+ node/peer_manager,
+ factory/builder,
+ common/utils/nat,
+ waku_relay,
+ waku_store/common,
+ ],
+ ./config_chat2
+
+import libp2p/protocols/pubsub/rpc/messages, libp2p/protocols/pubsub/pubsub
+import ../../waku/waku_rln_relay
+
+const Help =
+ """
+ Commands: /[?|help|connect|nick|exit]
+ help: Prints this help
+ connect: dials a remote peer
+ nick: change nickname for current chat session
+ exit: exits chat session
+"""
+
+# XXX Connected is a bit annoying, because incoming connections don't trigger state change
+# Could poll connection pool or something here, I suppose
+# TODO Ensure connected turns true on incoming connections, or get rid of it
+type Chat = ref object
+ node: WakuNode # waku node for publishing, subscribing, etc
+ transp: StreamTransport # transport streams between read & write file descriptor
+ subscribed: bool # indicates if a node is subscribed or not to a topic
+ connected: bool # if the node is connected to another peer
+ started: bool # if the node has started
+ nick: string # nickname for this chat session
+ prompt: bool # chat prompt is showing
+ contentTopic: string # default content topic for chat messages
+
+type
+ PrivateKey* = crypto.PrivateKey
+ Topic* = waku_core.PubsubTopic
+
+#####################
+## chat2 protobufs ##
+#####################
+
+type
+ SelectResult*[T] = Result[T, string]
+
+ Chat2Message* = object
+ timestamp*: int64
+ nick*: string
+ payload*: seq[byte]
+
+proc init*(T: type Chat2Message, buffer: seq[byte]): ProtoResult[T] =
+ var msg = Chat2Message()
+ let pb = initProtoBuffer(buffer)
+
+ var timestamp: uint64
+ discard ?pb.getField(1, timestamp)
+ msg.timestamp = int64(timestamp)
+
+ discard ?pb.getField(2, msg.nick)
+ discard ?pb.getField(3, msg.payload)
+
+ ok(msg)
+
+proc encode*(message: Chat2Message): ProtoBuffer =
+ var serialised = initProtoBuffer()
+
+ serialised.write(1, uint64(message.timestamp))
+ serialised.write(2, message.nick)
+ serialised.write(3, message.payload)
+
+ return serialised
+
+proc toString*(message: Chat2Message): string =
+ # Get message date and timestamp in local time
+ let time = message.timestamp.fromUnix().local().format("'<'MMM' 'dd,' 'HH:mm'>'")
+
+ return time & " " & message.nick & ": " & string.fromBytes(message.payload)
+
+#####################
+
+proc connectToNodes(c: Chat, nodes: seq[string]) {.async.} =
+ echo "Connecting to nodes"
+ await c.node.connectToNodes(nodes)
+ c.connected = true
+
+proc showChatPrompt(c: Chat) =
+ if not c.prompt:
+ try:
+ stdout.write(">> ")
+ stdout.flushFile()
+ c.prompt = true
+ except IOError:
+ discard
+
+proc getChatLine(c: Chat, msg: WakuMessage): Result[string, string] =
+ # No payload encoding/encryption from Waku
+ let
+ pb = Chat2Message.init(msg.payload)
+ chatLine =
+ if pb.isOk:
+ pb[].toString()
+ else:
+ string.fromBytes(msg.payload)
+ return ok(chatline)
+
+proc printReceivedMessage(c: Chat, msg: WakuMessage) =
+ let
+ pb = Chat2Message.init(msg.payload)
+ chatLine =
+ if pb.isOk:
+ pb[].toString()
+ else:
+ string.fromBytes(msg.payload)
+ try:
+ echo &"{chatLine}"
+ except ValueError:
+ # Formatting fail. Print chat line in any case.
+ echo chatLine
+
+ c.prompt = false
+ showChatPrompt(c)
+ trace "Printing message",
+ topic = DefaultPubsubTopic, chatLine, contentTopic = msg.contentTopic
+
+proc readNick(transp: StreamTransport): Future[string] {.async.} =
+ # Chat prompt
+ stdout.write("Choose a nickname >> ")
+ stdout.flushFile()
+ return await transp.readLine()
+
+proc startMetricsServer(
+ serverIp: IpAddress, serverPort: Port
+): Result[MetricsHttpServerRef, string] =
+ info "Starting metrics HTTP server", serverIp = $serverIp, serverPort = $serverPort
+
+ let metricsServerRes = MetricsHttpServerRef.new($serverIp, serverPort)
+ if metricsServerRes.isErr():
+ return err("metrics HTTP server start failed: " & $metricsServerRes.error)
+
+ let server = metricsServerRes.value
+ try:
+ waitFor server.start()
+ except CatchableError:
+ return err("metrics HTTP server start failed: " & getCurrentExceptionMsg())
+
+ info "Metrics HTTP server started", serverIp = $serverIp, serverPort = $serverPort
+ ok(metricsServerRes.value)
+
+proc publish(c: Chat, line: string) =
+ # First create a Chat2Message protobuf with this line of text
+ let time = getTime().toUnix()
+ let chat2pb =
+ Chat2Message(timestamp: time, nick: c.nick, payload: line.toBytes()).encode()
+
+ ## @TODO: error handling on failure
+ proc handler(response: PushResponse) {.gcsafe, closure.} =
+ trace "lightpush response received", response = response
+
+ var message = WakuMessage(
+ payload: chat2pb.buffer,
+ contentTopic: c.contentTopic,
+ version: 0,
+ timestamp: getNanosecondTime(time),
+ )
+ if not isNil(c.node.wakuRlnRelay):
+ # for future version when we support more than one rln protected content topic,
+ # we should check the message content topic as well
+ let appendRes = c.node.wakuRlnRelay.appendRLNProof(message, float64(time))
+ if appendRes.isErr():
+ debug "could not append rate limit proof to the message"
+ else:
+ debug "rate limit proof is appended to the message"
+ let decodeRes = RateLimitProof.init(message.proof)
+ if decodeRes.isErr():
+ error "could not decode the RLN proof"
+
+ let proof = decodeRes.get()
+ # TODO move it to log after dogfooding
+ let msgEpoch = fromEpoch(proof.epoch)
+ if fromEpoch(c.node.wakuRlnRelay.lastEpoch) == msgEpoch:
+ echo "--rln epoch: ",
+ msgEpoch, " ⚠️ message rate violation! you are spamming the network!"
+ else:
+ echo "--rln epoch: ", msgEpoch
+ # update the last epoch
+ c.node.wakuRlnRelay.lastEpoch = proof.epoch
+
+ try:
+ if not c.node.wakuLegacyLightPush.isNil():
+ # Attempt lightpush
+ (waitFor c.node.legacyLightpushPublish(some(DefaultPubsubTopic), message)).isOkOr:
+ error "failed to publish lightpush message", error = error
+ else:
+ (waitFor c.node.publish(some(DefaultPubsubTopic), message)).isOkOr:
+ error "failed to publish message", error = error
+ except CatchableError:
+ error "caught error publishing message: ", error = getCurrentExceptionMsg()
+
+# TODO This should read or be subscribe handler subscribe
+proc readAndPrint(c: Chat) {.async.} =
+ while true:
+ # while p.connected:
+ # # TODO: echo &"{p.id} -> "
+ #
+ # echo cast[string](await p.conn.readLp(1024))
+ #echo "readAndPrint subscribe NYI"
+ await sleepAsync(100.millis)
+
+# TODO Implement
+proc writeAndPrint(c: Chat) {.async.} =
+ while true:
+ # Connect state not updated on incoming WakuRelay connections
+ # if not c.connected:
+ # echo "type an address or wait for a connection:"
+ # echo "type /[help|?] for help"
+
+ # Chat prompt
+ showChatPrompt(c)
+
+ let line = await c.transp.readLine()
+ if line.startsWith("/help") or line.startsWith("/?") or not c.started:
+ echo Help
+ continue
+
+ # if line.startsWith("/disconnect"):
+ # echo "Ending current session"
+ # if p.connected and p.conn.closed.not:
+ # await p.conn.close()
+ # p.connected = false
+ elif line.startsWith("/connect"):
+ # TODO Should be able to connect to multiple peers for Waku chat
+ if c.connected:
+ echo "already connected to at least one peer"
+ continue
+
+ echo "enter address of remote peer"
+ let address = await c.transp.readLine()
+ if address.len > 0:
+ await c.connectToNodes(@[address])
+ elif line.startsWith("/nick"):
+ # Set a new nickname
+ c.nick = await readNick(c.transp)
+ echo "You are now known as " & c.nick
+ elif line.startsWith("/exit"):
+ echo "quitting..."
+
+ try:
+ await c.node.stop()
+ except:
+ echo "exception happened when stopping: " & getCurrentExceptionMsg()
+
+ quit(QuitSuccess)
+ else:
+ # XXX connected state problematic
+ if c.started:
+ c.publish(line)
+ # TODO Connect to peer logic?
+ else:
+ try:
+ if line.startsWith("/") and "p2p" in line:
+ await c.connectToNodes(@[line])
+ except:
+ echo &"unable to dial remote peer {line}"
+ echo getCurrentExceptionMsg()
+
+proc readWriteLoop(c: Chat) {.async.} =
+ asyncSpawn c.writeAndPrint() # execute the async function but does not block
+ asyncSpawn c.readAndPrint()
+
+proc readInput(wfd: AsyncFD) {.thread, raises: [Defect, CatchableError].} =
+ ## This procedure performs reading from `stdin` and sends data over
+ ## pipe to main thread.
+ let transp = fromPipe(wfd)
+
+ while true:
+ let line = stdin.readLine()
+ discard waitFor transp.write(line & "\r\n")
+
+{.pop.}
+ # @TODO confutils.nim(775, 17) Error: can raise an unlisted exception: ref IOError
+proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
+ let
+ transp = fromPipe(rfd)
+ conf = Chat2Conf.load()
+ nodekey =
+ if conf.nodekey.isSome():
+ conf.nodekey.get()
+ else:
+ PrivateKey.random(Secp256k1, rng[]).tryGet()
+
+ # set log level
+ if conf.logLevel != LogLevel.NONE:
+ setLogLevel(conf.logLevel)
+
+ let natRes = setupNat(
+ conf.nat,
+ clientId,
+ Port(uint16(conf.tcpPort) + conf.portsShift),
+ Port(uint16(conf.udpPort) + conf.portsShift),
+ )
+
+ if natRes.isErr():
+ raise newException(ValueError, "setupNat error " & natRes.error)
+
+ let (extIp, extTcpPort, extUdpPort) = natRes.get()
+
+ var enrBuilder = EnrBuilder.init(nodeKey)
+
+ let recordRes = enrBuilder.build()
+ let record =
+ if recordRes.isErr():
+ error "failed to create enr record", error = recordRes.error
+ quit(QuitFailure)
+ else:
+ recordRes.get()
+
+ let node = block:
+ var builder = WakuNodeBuilder.init()
+ builder.withNodeKey(nodeKey)
+ builder.withRecord(record)
+
+ builder
+ .withNetworkConfigurationDetails(
+ conf.listenAddress,
+ Port(uint16(conf.tcpPort) + conf.portsShift),
+ extIp,
+ extTcpPort,
+ wsBindPort = Port(uint16(conf.websocketPort) + conf.portsShift),
+ wsEnabled = conf.websocketSupport,
+ wssEnabled = conf.websocketSecureSupport,
+ )
+ .tryGet()
+ builder.build().tryGet()
+
+ await node.start()
+
+ if conf.rlnRelayCredPath == "":
+ raise newException(ConfigurationError, "rln-relay-cred-path MUST be passed")
+
+ if conf.relay:
+ let shards =
+ conf.shards.mapIt(RelayShard(clusterId: conf.clusterId, shardId: uint16(it)))
+ (await node.mountRelay()).isOkOr:
+ echo "failed to mount relay: " & error
+ return
+
+ await node.mountLibp2pPing()
+
+ let nick = await readNick(transp)
+ echo "Welcome, " & nick & "!"
+
+ var chat = Chat(
+ node: node,
+ transp: transp,
+ subscribed: true,
+ connected: false,
+ started: true,
+ nick: nick,
+ prompt: false,
+ contentTopic: conf.contentTopic,
+ )
+
+ if conf.staticnodes.len > 0:
+ echo "Connecting to static peers..."
+ await connectToNodes(chat, conf.staticnodes)
+
+ var dnsDiscoveryUrl = none(string)
+
+ if conf.fleet != Fleet.none:
+ # Use DNS discovery to connect to selected fleet
+ echo "Connecting to " & $conf.fleet & " fleet using DNS discovery..."
+
+ if conf.fleet == Fleet.test:
+ dnsDiscoveryUrl = some(
+ "enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im"
+ )
+ else:
+ # Connect to sandbox by default
+ dnsDiscoveryUrl = some(
+ "enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im"
+ )
+ elif conf.dnsDiscoveryUrl != "":
+ # No pre-selected fleet. Discover nodes via DNS using user config
+ debug "Discovering nodes using Waku DNS discovery", url = conf.dnsDiscoveryUrl
+ dnsDiscoveryUrl = some(conf.dnsDiscoveryUrl)
+
+ var discoveredNodes: seq[RemotePeerInfo]
+
+ if dnsDiscoveryUrl.isSome:
+ var nameServers: seq[TransportAddress]
+ for ip in conf.dnsAddrsNameServers:
+ nameServers.add(initTAddress(ip, Port(53))) # Assume all servers use port 53
+
+ let dnsResolver = DnsResolver.new(nameServers)
+
+ proc resolver(domain: string): Future[string] {.async, gcsafe.} =
+ trace "resolving", domain = domain
+ let resolved = await dnsResolver.resolveTxt(domain)
+ return resolved[0] # Use only first answer
+
+ var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl.get(), resolver)
+ if wakuDnsDiscovery.isOk:
+ let discoveredPeers = await wakuDnsDiscovery.get().findPeers()
+ if discoveredPeers.isOk:
+ info "Connecting to discovered peers"
+ discoveredNodes = discoveredPeers.get()
+ echo "Discovered and connecting to " & $discoveredNodes
+ waitFor chat.node.connectToNodes(discoveredNodes)
+ else:
+ warn "Failed to init Waku DNS discovery"
+
+ let peerInfo = node.switch.peerInfo
+ let listenStr = $peerInfo.addrs[0] & "/p2p/" & $peerInfo.peerId
+ echo &"Listening on\n {listenStr}"
+
+ if (conf.storenode != "") or (conf.store == true):
+ await node.mountStore()
+
+ var storenode: Option[RemotePeerInfo]
+
+ if conf.storenode != "":
+ let peerInfo = parsePeerInfo(conf.storenode)
+ if peerInfo.isOk():
+ storenode = some(peerInfo.value)
+ else:
+ error "Incorrect conf.storenode", error = peerInfo.error
+ elif discoveredNodes.len > 0:
+ echo "Store enabled, but no store nodes configured. Choosing one at random from discovered peers"
+ storenode = some(discoveredNodes[rand(0 .. len(discoveredNodes) - 1)])
+
+ if storenode.isSome():
+ # We have a viable storenode. Let's query it for historical messages.
+ echo "Connecting to storenode: " & $(storenode.get())
+
+ node.mountStoreClient()
+ node.peerManager.addServicePeer(storenode.get(), WakuStoreCodec)
+
+ proc storeHandler(response: StoreQueryResponse) {.gcsafe.} =
+ for msg in response.messages:
+ let payload =
+ if msg.message.isSome():
+ msg.message.get().payload
+ else:
+ newSeq[byte](0)
+
+ let
+ pb = Chat2Message.init(payload)
+ chatLine =
+ if pb.isOk:
+ pb[].toString()
+ else:
+ string.fromBytes(payload)
+ echo &"{chatLine}"
+ info "Hit store handler"
+
+ let queryRes = await node.query(
+ StoreQueryRequest(contentTopics: @[chat.contentTopic]), storenode.get()
+ )
+ if queryRes.isOk():
+ storeHandler(queryRes.value)
+
+ # NOTE Must be mounted after relay
+ if conf.lightpushnode != "":
+ let peerInfo = parsePeerInfo(conf.lightpushnode)
+ if peerInfo.isOk():
+ await mountLegacyLightPush(node)
+ node.mountLegacyLightPushClient()
+ node.peerManager.addServicePeer(peerInfo.value, WakuLightpushCodec)
+ else:
+ error "LightPush not mounted. Couldn't parse conf.lightpushnode",
+ error = peerInfo.error
+
+ if conf.filternode != "":
+ let peerInfo = parsePeerInfo(conf.filternode)
+ if peerInfo.isOk():
+ await node.mountFilter()
+ await node.mountFilterClient()
+
+ proc filterHandler(
+ pubsubTopic: PubsubTopic, msg: WakuMessage
+ ) {.async, gcsafe, closure.} =
+ trace "Hit filter handler", contentTopic = msg.contentTopic
+ chat.printReceivedMessage(msg)
+
+ # TODO: Here to support FilterV2 relevant subscription.
+ else:
+ error "Filter not mounted. Couldn't parse conf.filternode", error = peerInfo.error
+
+ # Subscribe to a topic, if relay is mounted
+ if conf.relay:
+ proc handler(topic: PubsubTopic, msg: WakuMessage): Future[void] {.async, gcsafe.} =
+ trace "Hit subscribe handler", topic
+
+ if msg.contentTopic == chat.contentTopic:
+ chat.printReceivedMessage(msg)
+
+ node.subscribe(
+ (kind: PubsubSub, topic: DefaultPubsubTopic), WakuRelayHandler(handler)
+ ).isOkOr:
+ error "failed to subscribe to pubsub topic",
+ topic = DefaultPubsubTopic, error = error
+
+ if conf.rlnRelay:
+ info "WakuRLNRelay is enabled"
+
+ proc spamHandler(wakuMessage: WakuMessage) {.gcsafe, closure.} =
+ debug "spam handler is called"
+ let chatLineResult = chat.getChatLine(wakuMessage)
+ if chatLineResult.isOk():
+ echo "A spam message is found and discarded : ", chatLineResult.value
+ else:
+ echo "A spam message is found and discarded"
+ chat.prompt = false
+ showChatPrompt(chat)
+
+ echo "rln-relay preparation is in progress..."
+
+ let rlnConf = WakuRlnConfig(
+ dynamic: conf.rlnRelayDynamic,
+ credIndex: conf.rlnRelayCredIndex,
+ chainId: UInt256.fromBytesBE(conf.rlnRelayChainId.toBytesBE()),
+ ethClientUrls: conf.ethClientUrls.mapIt(string(it)),
+ creds: some(
+ RlnRelayCreds(
+ path: conf.rlnRelayCredPath, password: conf.rlnRelayCredPassword
+ )
+ ),
+ userMessageLimit: conf.rlnRelayUserMessageLimit,
+ epochSizeSec: conf.rlnEpochSizeSec,
+ )
+
+ waitFor node.mountRlnRelay(rlnConf, spamHandler = some(spamHandler))
+
+ let membershipIndex = node.wakuRlnRelay.groupManager.membershipIndex.get()
+ let identityCredential = node.wakuRlnRelay.groupManager.idCredentials.get()
+ echo "your membership index is: ", membershipIndex
+ echo "your rln identity commitment key is: ",
+ identityCredential.idCommitment.inHex()
+ else:
+ info "WakuRLNRelay is disabled"
+ echo "WakuRLNRelay is disabled, please enable it by passing in the --rln-relay flag"
+ if conf.metricsLogging:
+ startMetricsLog()
+
+ if conf.metricsServer:
+ let metricsServer = startMetricsServer(
+ conf.metricsServerAddress, Port(conf.metricsServerPort + conf.portsShift)
+ )
+
+ await chat.readWriteLoop()
+
+ runForever()
+
+proc main(rng: ref HmacDrbgContext) {.async.} =
+ let (rfd, wfd) = createAsyncPipe()
+ if rfd == asyncInvalidPipe or wfd == asyncInvalidPipe:
+ raise newException(ValueError, "Could not initialize pipe!")
+
+ var thread: Thread[AsyncFD]
+ thread.createThread(readInput, wfd)
+ try:
+ await processInput(rfd, rng)
+ # Handle only ConfigurationError for now
+ # TODO: Throw other errors from the mounting procedure
+ except ConfigurationError as e:
+ raise e
+
+when isMainModule: # isMainModule = true when the module is compiled as the main file
+ let rng = crypto.newRng()
+ try:
+ waitFor(main(rng))
+ except CatchableError as e:
+ raise e
+
+## Dump of things that can be improved:
+##
+## - Incoming dialed peer does not change connected state (not relying on it for now)
+## - Unclear if staticnode argument works (can enter manually)
+## - Don't trigger self / double publish own messages
+## - Integrate store protocol (fetch messages in beginning)
+## - Integrate filter protocol (default/option to be light node, connect to filter node)
+## - Test/default to cluster node connection (diff protocol version)
+## - Redirect logs to separate file
+## - Expose basic publish/subscribe etc commands with /syntax
+## - Show part of peerid to know who sent message
+## - Deal with protobuf messages (e.g. other chat protocol, or encrypted)
diff --git a/third-party/nwaku/apps/chat2/config_chat2.nim b/third-party/nwaku/apps/chat2/config_chat2.nim
new file mode 100644
index 0000000..fe7865c
--- /dev/null
+++ b/third-party/nwaku/apps/chat2/config_chat2.nim
@@ -0,0 +1,351 @@
+import
+ chronicles,
+ chronos,
+ confutils,
+ confutils/defs,
+ confutils/std/net,
+ eth/keys,
+ libp2p/crypto/crypto,
+ libp2p/crypto/secp,
+ nimcrypto/utils,
+ std/strutils,
+ regex
+import waku/waku_core
+
+type
+ Fleet* = enum
+ none
+ prod
+ test
+
+ EthRpcUrl* = distinct string
+
+ Chat2Conf* = object ## General node config
+ logLevel* {.
+ desc: "Sets the log level.", defaultValue: LogLevel.INFO, name: "log-level"
+ .}: LogLevel
+
+ nodekey* {.desc: "P2P node private key as 64 char hex string.", name: "nodekey".}:
+ Option[crypto.PrivateKey]
+
+ listenAddress* {.
+ defaultValue: defaultListenAddress(config),
+ desc: "Listening address for the LibP2P traffic.",
+ name: "listen-address"
+ .}: IpAddress
+
+ tcpPort* {.desc: "TCP listening port.", defaultValue: 60000, name: "tcp-port".}:
+ Port
+
+ udpPort* {.desc: "UDP listening port.", defaultValue: 60000, name: "udp-port".}:
+ Port
+
+ portsShift* {.
+ desc: "Add a shift to all port numbers.", defaultValue: 0, name: "ports-shift"
+ .}: uint16
+
+ nat* {.
+ desc:
+ "Specify method to use for determining public address. " &
+ "Must be one of: any, none, upnp, pmp, extip:.",
+ defaultValue: "any"
+ .}: string
+
+ ## Persistence config
+ dbPath* {.
+ desc: "The database path for peristent storage", defaultValue: "", name: "db-path"
+ .}: string
+
+ persistPeers* {.
+ desc: "Enable peer persistence: true|false",
+ defaultValue: false,
+ name: "persist-peers"
+ .}: bool
+
+ persistMessages* {.
+ desc: "Enable message persistence: true|false",
+ defaultValue: false,
+ name: "persist-messages"
+ .}: bool
+
+ ## Relay config
+ relay* {.
+ desc: "Enable relay protocol: true|false", defaultValue: true, name: "relay"
+ .}: bool
+
+ staticnodes* {.
+ desc: "Peer multiaddr to directly connect with. Argument may be repeated.",
+ name: "staticnode"
+ .}: seq[string]
+
+ keepAlive* {.
+ desc: "Enable keep-alive for idle connections: true|false",
+ defaultValue: false,
+ name: "keep-alive"
+ .}: bool
+
+ clusterId* {.
+ desc:
+ "Cluster id that the node is running in. Node in a different cluster id is disconnected.",
+ defaultValue: 0,
+ name: "cluster-id"
+ .}: uint16
+
+ shards* {.
+ desc:
+ "Shards index to subscribe to [0..NUM_SHARDS_IN_NETWORK-1]. Argument may be repeated.",
+ defaultValue: @[uint16(0)],
+ name: "shard"
+ .}: seq[uint16]
+
+ ## Store config
+ store* {.
+ desc: "Enable store protocol: true|false", defaultValue: true, name: "store"
+ .}: bool
+
+ storenode* {.
+ desc: "Peer multiaddr to query for storage.", defaultValue: "", name: "storenode"
+ .}: string
+
+ ## Filter config
+ filter* {.
+ desc: "Enable filter protocol: true|false", defaultValue: false, name: "filter"
+ .}: bool
+
+ filternode* {.
+ desc: "Peer multiaddr to request content filtering of messages.",
+ defaultValue: "",
+ name: "filternode"
+ .}: string
+
+ ## Lightpush config
+ lightpush* {.
+ desc: "Enable lightpush protocol: true|false",
+ defaultValue: false,
+ name: "lightpush"
+ .}: bool
+
+ lightpushnode* {.
+ desc: "Peer multiaddr to request lightpush of published messages.",
+ defaultValue: "",
+ name: "lightpushnode"
+ .}: string
+
+ ## Metrics config
+ metricsServer* {.
+ desc: "Enable the metrics server: true|false",
+ defaultValue: false,
+ name: "metrics-server"
+ .}: bool
+
+ metricsServerAddress* {.
+ desc: "Listening address of the metrics server.",
+ defaultValue: parseIpAddress("127.0.0.1"),
+ name: "metrics-server-address"
+ .}: IpAddress
+
+ metricsServerPort* {.
+ desc: "Listening HTTP port of the metrics server.",
+ defaultValue: 8008,
+ name: "metrics-server-port"
+ .}: uint16
+
+ metricsLogging* {.
+ desc: "Enable metrics logging: true|false",
+ defaultValue: true,
+ name: "metrics-logging"
+ .}: bool
+
+ ## DNS discovery config
+ dnsDiscovery* {.
+ desc:
+ "Deprecated, please set dns-discovery-url instead. Enable discovering nodes via DNS",
+ defaultValue: false,
+ name: "dns-discovery"
+ .}: bool
+
+ dnsDiscoveryUrl* {.
+ desc: "URL for DNS node list in format 'enrtree://@'",
+ defaultValue: "",
+ name: "dns-discovery-url"
+ .}: string
+
+ dnsAddrsNameServers* {.
+ desc:
+ "DNS name server IPs to query for DNS multiaddrs resolution. Argument may be repeated.",
+ defaultValue: @[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")],
+ name: "dns-addrs-name-server"
+ .}: seq[IpAddress]
+
+ ## Chat2 configuration
+ fleet* {.
+ desc:
+ "Select the fleet to connect to. This sets the DNS discovery URL to the selected fleet.",
+ defaultValue: Fleet.prod,
+ name: "fleet"
+ .}: Fleet
+
+ contentTopic* {.
+ desc: "Content topic for chat messages.",
+ defaultValue: "/toy-chat/2/huilong/proto",
+ name: "content-topic"
+ .}: string
+
+ ## Websocket Configuration
+ websocketSupport* {.
+ desc: "Enable websocket: true|false",
+ defaultValue: false,
+ name: "websocket-support"
+ .}: bool
+
+ websocketPort* {.
+ desc: "WebSocket listening port.", defaultValue: 8000, name: "websocket-port"
+ .}: Port
+
+ websocketSecureSupport* {.
+ desc: "WebSocket Secure Support.",
+ defaultValue: false,
+ name: "websocket-secure-support"
+ .}: bool
+
+ ## rln-relay configuration
+ rlnRelay* {.
+ desc: "Enable spam protection through rln-relay: true|false",
+ defaultValue: false,
+ name: "rln-relay"
+ .}: bool
+
+ rlnRelayChainId* {.
+ desc:
+ "Chain ID of the provided contract (optional, will fetch from RPC provider if not used)",
+ defaultValue: 0,
+ name: "rln-relay-chain-id"
+ .}: uint
+
+ rlnRelayCredPath* {.
+ desc: "The path for peristing rln-relay credential",
+ defaultValue: "",
+ name: "rln-relay-cred-path"
+ .}: string
+
+ rlnRelayCredIndex* {.
+ desc: "the index of the onchain commitment to use", name: "rln-relay-cred-index"
+ .}: Option[uint]
+
+ rlnRelayDynamic* {.
+ desc: "Enable waku-rln-relay with on-chain dynamic group management: true|false",
+ defaultValue: false,
+ name: "rln-relay-dynamic"
+ .}: bool
+
+ rlnRelayIdKey* {.
+ desc: "Rln relay identity secret key as a Hex string",
+ defaultValue: "",
+ name: "rln-relay-id-key"
+ .}: string
+
+ rlnRelayIdCommitmentKey* {.
+ desc: "Rln relay identity commitment key as a Hex string",
+ defaultValue: "",
+ name: "rln-relay-id-commitment-key"
+ .}: string
+
+ ethClientUrls* {.
+ desc:
+ "HTTP address of an Ethereum testnet client e.g., http://localhost:8540/. Argument may be repeated.",
+ defaultValue: newSeq[EthRpcUrl](0),
+ name: "rln-relay-eth-client-address"
+ .}: seq[EthRpcUrl]
+
+ rlnRelayEthContractAddress* {.
+ desc: "Address of membership contract on an Ethereum testnet",
+ defaultValue: "",
+ name: "rln-relay-eth-contract-address"
+ .}: string
+
+ rlnRelayCredPassword* {.
+ desc: "Password for encrypting RLN credentials",
+ defaultValue: "",
+ name: "rln-relay-cred-password"
+ .}: string
+
+ rlnRelayUserMessageLimit* {.
+ desc:
+ "Set a user message limit for the rln membership registration. Must be a positive integer. Default is 1.",
+ defaultValue: 1,
+ name: "rln-relay-user-message-limit"
+ .}: uint64
+
+ rlnEpochSizeSec* {.
+ desc:
+ "Epoch size in seconds used to rate limit RLN memberships. Default is 1 second.",
+ defaultValue: 1,
+ name: "rln-relay-epoch-sec"
+ .}: uint64
+
+# NOTE: Keys are different in nim-libp2p
+proc parseCmdArg*(T: type crypto.PrivateKey, p: string): T =
+ try:
+ let key = SkPrivateKey.init(utils.fromHex(p)).tryGet()
+ # XXX: Here at the moment
+ result = crypto.PrivateKey(scheme: Secp256k1, skkey: key)
+ except CatchableError as e:
+ raise newException(ValueError, "Invalid private key")
+
+proc completeCmdArg*(T: type crypto.PrivateKey, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type IpAddress, p: string): T =
+ try:
+ result = parseIpAddress(p)
+ except CatchableError as e:
+ raise newException(ValueError, "Invalid IP address")
+
+proc completeCmdArg*(T: type IpAddress, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type Port, p: string): T =
+ try:
+ result = Port(parseInt(p))
+ except CatchableError as e:
+ raise newException(ValueError, "Invalid Port number")
+
+proc completeCmdArg*(T: type Port, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type Option[uint], p: string): T =
+ try:
+ some(parseUint(p))
+ except CatchableError:
+ raise newException(ValueError, "Invalid unsigned integer")
+
+proc completeCmdArg*(T: type EthRpcUrl, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type EthRpcUrl, s: string): T =
+ ## allowed patterns:
+ ## http://url:port
+ ## https://url:port
+ ## http://url:port/path
+ ## https://url:port/path
+ ## http://url/with/path
+ ## http://url:port/path?query
+ ## https://url:port/path?query
+ ## disallowed patterns:
+ ## any valid/invalid ws or wss url
+ var httpPattern =
+ re2"^(https?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*"
+ var wsPattern =
+ re2"^(wss?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*"
+ if regex.match(s, wsPattern):
+ raise newException(
+ ValueError, "Websocket RPC URL is not supported, Please use an HTTP URL"
+ )
+ if not regex.match(s, httpPattern):
+ raise newException(ValueError, "Invalid HTTP RPC URL")
+ return EthRpcUrl(s)
+
+func defaultListenAddress*(conf: Chat2Conf): IpAddress =
+ # TODO: How should we select between IPv4 and IPv6
+ # Maybe there should be a config option for this.
+ (static parseIpAddress("0.0.0.0"))
diff --git a/third-party/nwaku/apps/chat2/nim.cfg b/third-party/nwaku/apps/chat2/nim.cfg
new file mode 100644
index 0000000..2231f2e
--- /dev/null
+++ b/third-party/nwaku/apps/chat2/nim.cfg
@@ -0,0 +1,4 @@
+-d:chronicles_line_numbers
+-d:chronicles_runtime_filtering:on
+-d:discv5_protocol_id:d5waku
+path = "../.."
diff --git a/third-party/nwaku/apps/chat2bridge/chat2bridge.nim b/third-party/nwaku/apps/chat2bridge/chat2bridge.nim
new file mode 100644
index 0000000..c2bf9c0
--- /dev/null
+++ b/third-party/nwaku/apps/chat2bridge/chat2bridge.nim
@@ -0,0 +1,328 @@
+{.push raises: [].}
+
+import
+ std/[tables, times, strutils, hashes, sequtils, json],
+ chronos,
+ confutils,
+ chronicles,
+ chronicles/topics_registry,
+ chronos/streams/tlsstream,
+ metrics,
+ metrics/chronos_httpserver,
+ stew/byteutils,
+ eth/net/nat,
+ # Matterbridge client imports
+ # Waku v2 imports
+ libp2p/crypto/crypto,
+ libp2p/errors,
+ waku/[
+ waku_core,
+ waku_node,
+ node/peer_manager,
+ waku_filter_v2,
+ waku_store,
+ factory/builder,
+ common/utils/matterbridge_client,
+ common/rate_limit/setting,
+ ],
+ # Chat 2 imports
+ ../chat2/chat2,
+ # Common cli config
+ ./config_chat2bridge
+
+declarePublicCounter chat2_mb_transfers,
+ "Number of messages transferred between chat2 and Matterbridge", ["type"]
+declarePublicCounter chat2_mb_dropped, "Number of messages dropped", ["reason"]
+
+logScope:
+ topics = "chat2bridge"
+
+##################
+# Default values #
+##################
+
+const DeduplQSize = 20 # Maximum number of seen messages to keep in deduplication queue
+
+#########
+# Types #
+#########
+
+type
+ Chat2MatterBridge* = ref object of RootObj
+ mbClient*: MatterbridgeClient
+ nodev2*: WakuNode
+ running: bool
+ pollPeriod: chronos.Duration
+ seen: seq[Hash] #FIFO queue
+ contentTopic: string
+
+ MbMessageHandler = proc(jsonNode: JsonNode) {.async.}
+
+###################
+# Helper functions #
+###################S
+
+proc containsOrAdd(sequence: var seq[Hash], hash: Hash): bool =
+ if sequence.contains(hash):
+ return true
+
+ if sequence.len >= DeduplQSize:
+ trace "Deduplication queue full. Removing oldest item."
+ sequence.delete 0, 0 # Remove first item in queue
+
+ sequence.add(hash)
+
+ return false
+
+proc toWakuMessage(
+ cmb: Chat2MatterBridge, jsonNode: JsonNode
+): WakuMessage {.raises: [Defect, KeyError].} =
+ # Translates a Matterbridge API JSON response to a Waku v2 message
+ let msgFields = jsonNode.getFields()
+
+ # @TODO error handling here - verify expected fields
+
+ let chat2pb = Chat2Message(
+ timestamp: getTime().toUnix(), # @TODO use provided timestamp
+ nick: msgFields["username"].getStr(),
+ payload: msgFields["text"].getStr().toBytes(),
+ ).encode()
+
+ WakuMessage(payload: chat2pb.buffer, contentTopic: cmb.contentTopic, version: 0)
+
+proc toChat2(cmb: Chat2MatterBridge, jsonNode: JsonNode) {.async.} =
+ let msg = cmb.toWakuMessage(jsonNode)
+
+ if cmb.seen.containsOrAdd(msg.payload.hash()):
+ # This is a duplicate message. Return.
+ chat2_mb_dropped.inc(labelValues = ["duplicate"])
+ return
+
+ trace "Post Matterbridge message to chat2"
+
+ chat2_mb_transfers.inc(labelValues = ["mb_to_chat2"])
+
+ (await cmb.nodev2.publish(some(DefaultPubsubTopic), msg)).isOkOr:
+ error "failed to publish message", error = error
+
+proc toMatterbridge(
+ cmb: Chat2MatterBridge, msg: WakuMessage
+) {.gcsafe, raises: [Exception].} =
+ if cmb.seen.containsOrAdd(msg.payload.hash()):
+ # This is a duplicate message. Return.
+ chat2_mb_dropped.inc(labelValues = ["duplicate"])
+ return
+
+ if msg.contentTopic != cmb.contentTopic:
+ # Only bridge messages on the configured content topic
+ chat2_mb_dropped.inc(labelValues = ["filtered"])
+ return
+
+ trace "Post chat2 message to Matterbridge"
+
+ chat2_mb_transfers.inc(labelValues = ["chat2_to_mb"])
+
+ let chat2Msg = Chat2Message.init(msg.payload)
+
+ assert chat2Msg.isOk
+
+ let postRes = cmb.mbClient.postMessage(
+ text = string.fromBytes(chat2Msg[].payload), username = chat2Msg[].nick
+ )
+
+ if postRes.isErr() or (postRes[] == false):
+ chat2_mb_dropped.inc(labelValues = ["duplicate"])
+ error "Matterbridge host unreachable. Dropping message."
+
+proc pollMatterbridge(cmb: Chat2MatterBridge, handler: MbMessageHandler) {.async.} =
+ while cmb.running:
+ let getRes = cmb.mbClient.getMessages()
+
+ if getRes.isOk():
+ for jsonNode in getRes[]:
+ await handler(jsonNode)
+ else:
+ error "Matterbridge host unreachable. Sleeping before retrying."
+ await sleepAsync(chronos.seconds(10))
+
+ await sleepAsync(cmb.pollPeriod)
+
+##############
+# Public API #
+##############
+proc new*(
+ T: type Chat2MatterBridge,
+ # Matterbridge initialisation
+ mbHostUri: string,
+ mbGateway: string,
+ # NodeV2 initialisation
+ nodev2Key: crypto.PrivateKey,
+ nodev2BindIp: IpAddress,
+ nodev2BindPort: Port,
+ nodev2ExtIp = none[IpAddress](),
+ nodev2ExtPort = none[Port](),
+ contentTopic: string,
+): T {.
+ raises: [Defect, ValueError, KeyError, TLSStreamProtocolError, IOError, LPError]
+.} =
+ # Setup Matterbridge
+ let mbClient = MatterbridgeClient.new(mbHostUri, mbGateway)
+
+ # Let's verify the Matterbridge configuration before continuing
+ let clientHealth = mbClient.isHealthy()
+
+ if clientHealth.isOk() and clientHealth[]:
+ info "Reached Matterbridge host", host = mbClient.host
+ else:
+ raise newException(ValueError, "Matterbridge client not reachable/healthy")
+
+ # Setup Waku v2 node
+ let nodev2 = block:
+ var builder = WakuNodeBuilder.init()
+ builder.withNodeKey(nodev2Key)
+
+ builder
+ .withNetworkConfigurationDetails(
+ nodev2BindIp, nodev2BindPort, nodev2ExtIp, nodev2ExtPort
+ )
+ .tryGet()
+ builder.build().tryGet()
+
+ return Chat2MatterBridge(
+ mbClient: mbClient,
+ nodev2: nodev2,
+ running: false,
+ pollPeriod: chronos.seconds(1),
+ contentTopic: contentTopic,
+ )
+
+proc start*(cmb: Chat2MatterBridge) {.async.} =
+ info "Starting Chat2MatterBridge"
+
+ cmb.running = true
+
+ debug "Start polling Matterbridge"
+
+ # Start Matterbridge polling (@TODO: use streaming interface)
+ proc mbHandler(jsonNode: JsonNode) {.async.} =
+ trace "Bridging message from Matterbridge to chat2", jsonNode = jsonNode
+ waitFor cmb.toChat2(jsonNode)
+
+ asyncSpawn cmb.pollMatterbridge(mbHandler)
+
+ # Start Waku v2 node
+ debug "Start listening on Waku v2"
+ await cmb.nodev2.start()
+
+ # Always mount relay for bridge
+ # `triggerSelf` is false on a `bridge` to avoid duplicates
+ (await cmb.nodev2.mountRelay()).isOkOr:
+ error "failed to mount relay", error = error
+ return
+
+ cmb.nodev2.wakuRelay.triggerSelf = false
+
+ # Bridging
+ # Handle messages on Waku v2 and bridge to Matterbridge
+ proc relayHandler(
+ pubsubTopic: PubsubTopic, msg: WakuMessage
+ ): Future[void] {.async.} =
+ trace "Bridging message from Chat2 to Matterbridge", msg = msg
+ try:
+ cmb.toMatterbridge(msg)
+ except:
+ error "exception in relayHandler: " & getCurrentExceptionMsg()
+
+ cmb.nodev2.subscribe((kind: PubsubSub, topic: DefaultPubsubTopic), relayHandler).isOkOr:
+ error "failed to subscribe to relay", topic = DefaultPubsubTopic, error = error
+ return
+
+proc stop*(cmb: Chat2MatterBridge) {.async: (raises: [Exception]).} =
+ info "Stopping Chat2MatterBridge"
+
+ cmb.running = false
+
+ await cmb.nodev2.stop()
+
+{.pop.}
+ # @TODO confutils.nim(775, 17) Error: can raise an unlisted exception: ref IOError
+when isMainModule:
+ import waku/common/utils/nat, waku/waku_api/message_cache
+
+ let
+ rng = newRng()
+ conf = Chat2MatterbridgeConf.load()
+
+ if conf.logLevel != LogLevel.NONE:
+ setLogLevel(conf.logLevel)
+
+ let natRes = setupNat(
+ conf.nat,
+ clientId,
+ Port(uint16(conf.libp2pTcpPort) + conf.portsShift),
+ Port(uint16(conf.udpPort) + conf.portsShift),
+ )
+ if natRes.isErr():
+ error "Error in setupNat", error = natRes.error
+
+ # Load address configuration
+ let
+ (nodev2ExtIp, nodev2ExtPort, _) = natRes.get()
+ ## The following heuristic assumes that, in absence of manual
+ ## config, the external port is the same as the bind port.
+ extPort =
+ if nodev2ExtIp.isSome() and nodev2ExtPort.isNone():
+ some(Port(uint16(conf.libp2pTcpPort) + conf.portsShift))
+ else:
+ nodev2ExtPort
+
+ let bridge = Chat2Matterbridge.new(
+ mbHostUri = "http://" & $initTAddress(conf.mbHostAddress, Port(conf.mbHostPort)),
+ mbGateway = conf.mbGateway,
+ nodev2Key = conf.nodekey,
+ nodev2BindIp = conf.listenAddress,
+ nodev2BindPort = Port(uint16(conf.libp2pTcpPort) + conf.portsShift),
+ nodev2ExtIp = nodev2ExtIp,
+ nodev2ExtPort = extPort,
+ contentTopic = conf.contentTopic,
+ )
+
+ waitFor bridge.start()
+
+ # Now load rest of config
+ # Mount configured Waku v2 protocols
+ waitFor mountLibp2pPing(bridge.nodev2)
+
+ if conf.store:
+ waitFor mountStore(bridge.nodev2)
+
+ if conf.filter:
+ waitFor mountFilter(bridge.nodev2)
+
+ if conf.staticnodes.len > 0:
+ waitFor connectToNodes(bridge.nodev2, conf.staticnodes)
+
+ if conf.storenode != "":
+ let storePeer = parsePeerInfo(conf.storenode)
+ if storePeer.isOk():
+ bridge.nodev2.peerManager.addServicePeer(storePeer.value, WakuStoreCodec)
+ else:
+ error "Error parsing conf.storenode", error = storePeer.error
+
+ if conf.filternode != "":
+ let filterPeer = parsePeerInfo(conf.filternode)
+ if filterPeer.isOk():
+ bridge.nodev2.peerManager.addServicePeer(
+ filterPeer.value, WakuFilterSubscribeCodec
+ )
+ else:
+ error "Error parsing conf.filternode", error = filterPeer.error
+
+ if conf.metricsServer:
+ let
+ address = conf.metricsServerAddress
+ port = conf.metricsServerPort + conf.portsShift
+ info "Starting metrics HTTP server", address, port
+ startMetricsHttpServer($address, Port(port))
+
+ runForever()
diff --git a/third-party/nwaku/apps/chat2bridge/config_chat2bridge.nim b/third-party/nwaku/apps/chat2bridge/config_chat2bridge.nim
new file mode 100644
index 0000000..c7d8bb5
--- /dev/null
+++ b/third-party/nwaku/apps/chat2bridge/config_chat2bridge.nim
@@ -0,0 +1,148 @@
+import
+ confutils,
+ confutils/defs,
+ confutils/std/net,
+ chronicles,
+ chronos,
+ libp2p/crypto/[crypto, secp],
+ eth/keys
+
+type Chat2MatterbridgeConf* = object
+ logLevel* {.
+ desc: "Sets the log level", defaultValue: LogLevel.INFO, name: "log-level"
+ .}: LogLevel
+
+ listenAddress* {.
+ defaultValue: defaultListenAddress(config),
+ desc: "Listening address for the LibP2P traffic",
+ name: "listen-address"
+ .}: IpAddress
+
+ libp2pTcpPort* {.
+ desc: "Libp2p TCP listening port (for Waku v2)",
+ defaultValue: 9000,
+ name: "libp2p-tcp-port"
+ .}: uint16
+
+ udpPort* {.desc: "UDP listening port", defaultValue: 9000, name: "udp-port".}: uint16
+
+ portsShift* {.
+ desc: "Add a shift to all default port numbers",
+ defaultValue: 0,
+ name: "ports-shift"
+ .}: uint16
+
+ nat* {.
+ desc:
+ "Specify method to use for determining public address. " &
+ "Must be one of: any, none, upnp, pmp, extip:",
+ defaultValue: "any"
+ .}: string
+
+ metricsServer* {.
+ desc: "Enable the metrics server", defaultValue: false, name: "metrics-server"
+ .}: bool
+
+ metricsServerAddress* {.
+ desc: "Listening address of the metrics server",
+ defaultValue: parseIpAddress("127.0.0.1"),
+ name: "metrics-server-address"
+ .}: IpAddress
+
+ metricsServerPort* {.
+ desc: "Listening HTTP port of the metrics server",
+ defaultValue: 8008,
+ name: "metrics-server-port"
+ .}: uint16
+
+ ### Waku v2 options
+ staticnodes* {.
+ desc: "Multiaddr of peer to directly connect with. Argument may be repeated",
+ name: "staticnode"
+ .}: seq[string]
+
+ nodekey* {.
+ desc: "P2P node private key as hex",
+ defaultValue: crypto.PrivateKey.random(Secp256k1, newRng()[]).tryGet(),
+ name: "nodekey"
+ .}: crypto.PrivateKey
+
+ store* {.
+ desc: "Flag whether to start store protocol", defaultValue: true, name: "store"
+ .}: bool
+
+ filter* {.
+ desc: "Flag whether to start filter protocol", defaultValue: false, name: "filter"
+ .}: bool
+
+ relay* {.
+ desc: "Flag whether to start relay protocol", defaultValue: true, name: "relay"
+ .}: bool
+
+ storenode* {.
+ desc: "Multiaddr of peer to connect with for waku store protocol",
+ defaultValue: "",
+ name: "storenode"
+ .}: string
+
+ filternode* {.
+ desc: "Multiaddr of peer to connect with for waku filter protocol",
+ defaultValue: "",
+ name: "filternode"
+ .}: string
+
+ # Matterbridge options
+ mbHostAddress* {.
+ desc: "Listening address of the Matterbridge host",
+ defaultValue: parseIpAddress("127.0.0.1"),
+ name: "mb-host-address"
+ .}: IpAddress
+
+ mbHostPort* {.
+ desc: "Listening port of the Matterbridge host",
+ defaultValue: 4242,
+ name: "mb-host-port"
+ .}: uint16
+
+ mbGateway* {.
+ desc: "Matterbridge gateway", defaultValue: "gateway1", name: "mb-gateway"
+ .}: string
+
+ ## Chat2 options
+ contentTopic* {.
+ desc: "Content topic to bridge chat messages to.",
+ defaultValue: "/toy-chat/2/huilong/proto",
+ name: "content-topic"
+ .}: string
+
+proc parseCmdArg*(T: type keys.KeyPair, p: string): T =
+ try:
+ let privkey = keys.PrivateKey.fromHex(string(p)).tryGet()
+ result = privkey.toKeyPair()
+ except CatchableError:
+ raise newException(ValueError, "Invalid private key")
+
+proc completeCmdArg*(T: type keys.KeyPair, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type crypto.PrivateKey, p: string): T =
+ let key = SkPrivateKey.init(p)
+ if key.isOk():
+ crypto.PrivateKey(scheme: Secp256k1, skkey: key.get())
+ else:
+ raise newException(ValueError, "Invalid private key")
+
+proc completeCmdArg*(T: type crypto.PrivateKey, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type IpAddress, p: string): T =
+ try:
+ result = parseIpAddress(p)
+ except CatchableError:
+ raise newException(ValueError, "Invalid IP address")
+
+proc completeCmdArg*(T: type IpAddress, val: string): seq[string] =
+ return @[]
+
+func defaultListenAddress*(conf: Chat2MatterbridgeConf): IpAddress =
+ (parseIpAddress("0.0.0.0"))
diff --git a/third-party/nwaku/apps/chat2bridge/nim.cfg b/third-party/nwaku/apps/chat2bridge/nim.cfg
new file mode 100644
index 0000000..2231f2e
--- /dev/null
+++ b/third-party/nwaku/apps/chat2bridge/nim.cfg
@@ -0,0 +1,4 @@
+-d:chronicles_line_numbers
+-d:chronicles_runtime_filtering:on
+-d:discv5_protocol_id:d5waku
+path = "../.."
diff --git a/third-party/nwaku/apps/chat2mix/chat2mix.nim b/third-party/nwaku/apps/chat2mix/chat2mix.nim
new file mode 100644
index 0000000..e75ca75
--- /dev/null
+++ b/third-party/nwaku/apps/chat2mix/chat2mix.nim
@@ -0,0 +1,703 @@
+## chat2 is an example of usage of Waku v2. For suggested usage options, please
+## see dingpu tutorial in docs folder.
+
+when not (compileOption("threads")):
+ {.fatal: "Please, compile this program with the --threads:on option!".}
+
+{.push raises: [].}
+
+import std/[strformat, strutils, times, options, random, sequtils]
+import
+ confutils,
+ chronicles,
+ chronos,
+ eth/keys,
+ bearssl,
+ results,
+ stew/[byteutils],
+ metrics,
+ metrics/chronos_httpserver
+import
+ libp2p/[
+ switch, # manage transports, a single entry point for dialing and listening
+ crypto/crypto, # cryptographic functions
+ stream/connection, # create and close stream read / write connections
+ multiaddress,
+ # encode different addressing schemes. For example, /ip4/7.7.7.7/tcp/6543 means it is using IPv4 protocol and TCP
+ peerinfo,
+ # manage the information of a peer, such as peer ID and public / private key
+ peerid, # Implement how peers interact
+ protobuf/minprotobuf, # message serialisation/deserialisation from and to protobufs
+ nameresolving/dnsresolver,
+ ] # define DNS resolution
+import mix/curve25519
+import
+ waku/[
+ waku_core,
+ waku_lightpush/common,
+ waku_lightpush/rpc,
+ waku_enr,
+ discovery/waku_dnsdisc,
+ waku_node,
+ node/waku_metrics,
+ node/peer_manager,
+ factory/builder,
+ common/utils/nat,
+ waku_store/common,
+ waku_filter_v2/client,
+ common/logging,
+ ],
+ ./config_chat2mix
+
+import libp2p/protocols/pubsub/rpc/messages, libp2p/protocols/pubsub/pubsub
+import ../../waku/waku_rln_relay
+
+logScope:
+ topics = "chat2 mix"
+
+const Help =
+ """
+ Commands: /[?|help|connect|nick|exit]
+ help: Prints this help
+ connect: dials a remote peer
+ nick: change nickname for current chat session
+ exit: exits chat session
+"""
+
+# XXX Connected is a bit annoying, because incoming connections don't trigger state change
+# Could poll connection pool or something here, I suppose
+# TODO Ensure connected turns true on incoming connections, or get rid of it
+type Chat = ref object
+ node: WakuNode # waku node for publishing, subscribing, etc
+ transp: StreamTransport # transport streams between read & write file descriptor
+ subscribed: bool # indicates if a node is subscribed or not to a topic
+ connected: bool # if the node is connected to another peer
+ started: bool # if the node has started
+ nick: string # nickname for this chat session
+ prompt: bool # chat prompt is showing
+ contentTopic: string # default content topic for chat messages
+ conf: Chat2Conf # configuration for chat2
+
+type
+ PrivateKey* = crypto.PrivateKey
+ Topic* = waku_core.PubsubTopic
+
+#####################
+## chat2 protobufs ##
+#####################
+
+type
+ SelectResult*[T] = Result[T, string]
+
+ Chat2Message* = object
+ timestamp*: int64
+ nick*: string
+ payload*: seq[byte]
+
+proc getPubsubTopic*(
+ conf: Chat2Conf, node: WakuNode, contentTopic: string
+): PubsubTopic =
+ let shard = node.wakuAutoSharding.get().getShard(contentTopic).valueOr:
+ echo "Could not parse content topic: " & error
+ return "" #TODO: fix this.
+ return $RelayShard(clusterId: conf.clusterId, shardId: shard.shardId)
+
+proc init*(T: type Chat2Message, buffer: seq[byte]): ProtoResult[T] =
+ var msg = Chat2Message()
+ let pb = initProtoBuffer(buffer)
+
+ var timestamp: uint64
+ discard ?pb.getField(1, timestamp)
+ msg.timestamp = int64(timestamp)
+
+ discard ?pb.getField(2, msg.nick)
+ discard ?pb.getField(3, msg.payload)
+
+ ok(msg)
+
+proc encode*(message: Chat2Message): ProtoBuffer =
+ var serialised = initProtoBuffer()
+
+ serialised.write(1, uint64(message.timestamp))
+ serialised.write(2, message.nick)
+ serialised.write(3, message.payload)
+
+ return serialised
+
+proc toString*(message: Chat2Message): string =
+ # Get message date and timestamp in local time
+ let time = message.timestamp.fromUnix().local().format("'<'MMM' 'dd,' 'HH:mm'>'")
+
+ return time & " " & message.nick & ": " & string.fromBytes(message.payload)
+
+#####################
+
+proc connectToNodes(c: Chat, nodes: seq[string]) {.async.} =
+ echo "Connecting to nodes"
+ await c.node.connectToNodes(nodes)
+ c.connected = true
+
+proc showChatPrompt(c: Chat) =
+ if not c.prompt:
+ try:
+ stdout.write(">> ")
+ stdout.flushFile()
+ c.prompt = true
+ except IOError:
+ discard
+
+proc getChatLine(c: Chat, msg: WakuMessage): Result[string, string] =
+ # No payload encoding/encryption from Waku
+ let
+ pb = Chat2Message.init(msg.payload)
+ chatLine =
+ if pb.isOk:
+ pb[].toString()
+ else:
+ string.fromBytes(msg.payload)
+ return ok(chatline)
+
+proc printReceivedMessage(c: Chat, msg: WakuMessage) =
+ let
+ pb = Chat2Message.init(msg.payload)
+ chatLine =
+ if pb.isOk:
+ pb[].toString()
+ else:
+ string.fromBytes(msg.payload)
+ try:
+ echo &"{chatLine}"
+ except ValueError:
+ # Formatting fail. Print chat line in any case.
+ echo chatLine
+
+ c.prompt = false
+ showChatPrompt(c)
+ trace "Printing message", chatLine, contentTopic = msg.contentTopic
+
+proc readNick(transp: StreamTransport): Future[string] {.async.} =
+ # Chat prompt
+ stdout.write("Choose a nickname >> ")
+ stdout.flushFile()
+ return await transp.readLine()
+
+proc startMetricsServer(
+ serverIp: IpAddress, serverPort: Port
+): Result[MetricsHttpServerRef, string] =
+ info "Starting metrics HTTP server", serverIp = $serverIp, serverPort = $serverPort
+
+ let metricsServerRes = MetricsHttpServerRef.new($serverIp, serverPort)
+ if metricsServerRes.isErr():
+ return err("metrics HTTP server start failed: " & $metricsServerRes.error)
+
+ let server = metricsServerRes.value
+ try:
+ waitFor server.start()
+ except CatchableError:
+ return err("metrics HTTP server start failed: " & getCurrentExceptionMsg())
+
+ info "Metrics HTTP server started", serverIp = $serverIp, serverPort = $serverPort
+ ok(metricsServerRes.value)
+
+proc publish(c: Chat, line: string) {.async.} =
+ # First create a Chat2Message protobuf with this line of text
+ let time = getTime().toUnix()
+ let chat2pb =
+ Chat2Message(timestamp: time, nick: c.nick, payload: line.toBytes()).encode()
+
+ ## @TODO: error handling on failure
+ proc handler(response: LightPushResponse) {.gcsafe, closure.} =
+ trace "lightpush response received", response = response
+
+ var message = WakuMessage(
+ payload: chat2pb.buffer,
+ contentTopic: c.contentTopic,
+ version: 0,
+ timestamp: getNanosecondTime(time),
+ )
+
+ try:
+ if not c.node.wakuLightpushClient.isNil():
+ # Attempt lightpush with mix
+
+ (
+ waitFor c.node.lightpushPublish(
+ some(c.conf.getPubsubTopic(c.node, c.contentTopic)),
+ message,
+ none(RemotePeerInfo),
+ true,
+ )
+ ).isOkOr:
+ error "failed to publish lightpush message", error = error
+ else:
+ error "failed to publish message as lightpush client is not initialized"
+ except CatchableError:
+ error "caught error publishing message: ", error = getCurrentExceptionMsg()
+
+# TODO This should read or be subscribe handler subscribe
+proc readAndPrint(c: Chat) {.async.} =
+ while true:
+ # while p.connected:
+ # # TODO: echo &"{p.id} -> "
+ #
+ # echo cast[string](await p.conn.readLp(1024))
+ #echo "readAndPrint subscribe NYI"
+ await sleepAsync(100)
+
+# TODO Implement
+proc writeAndPrint(c: Chat) {.async.} =
+ while true:
+ # Connect state not updated on incoming WakuRelay connections
+ # if not c.connected:
+ # echo "type an address or wait for a connection:"
+ # echo "type /[help|?] for help"
+
+ # Chat prompt
+ showChatPrompt(c)
+
+ let line = await c.transp.readLine()
+ if line.startsWith("/help") or line.startsWith("/?") or not c.started:
+ echo Help
+ continue
+
+ # if line.startsWith("/disconnect"):
+ # echo "Ending current session"
+ # if p.connected and p.conn.closed.not:
+ # await p.conn.close()
+ # p.connected = false
+ elif line.startsWith("/connect"):
+ # TODO Should be able to connect to multiple peers for Waku chat
+ if c.connected:
+ echo "already connected to at least one peer"
+ continue
+
+ echo "enter address of remote peer"
+ let address = await c.transp.readLine()
+ if address.len > 0:
+ await c.connectToNodes(@[address])
+ elif line.startsWith("/nick"):
+ # Set a new nickname
+ c.nick = await readNick(c.transp)
+ echo "You are now known as " & c.nick
+ elif line.startsWith("/exit"):
+ echo "quitting..."
+
+ try:
+ await c.node.stop()
+ except:
+ echo "exception happened when stopping: " & getCurrentExceptionMsg()
+
+ quit(QuitSuccess)
+ else:
+ # XXX connected state problematic
+ if c.started:
+ echo "publishing message: " & line
+ await c.publish(line)
+ # TODO Connect to peer logic?
+ else:
+ try:
+ if line.startsWith("/") and "p2p" in line:
+ await c.connectToNodes(@[line])
+ except:
+ echo &"unable to dial remote peer {line}"
+ echo getCurrentExceptionMsg()
+
+proc readWriteLoop(c: Chat) {.async.} =
+ asyncSpawn c.writeAndPrint() # execute the async function but does not block
+ asyncSpawn c.readAndPrint()
+
+proc readInput(wfd: AsyncFD) {.thread, raises: [Defect, CatchableError].} =
+ ## This procedure performs reading from `stdin` and sends data over
+ ## pipe to main thread.
+ let transp = fromPipe(wfd)
+
+ while true:
+ let line = stdin.readLine()
+ discard waitFor transp.write(line & "\r\n")
+
+var alreadyUsedServicePeers {.threadvar.}: seq[RemotePeerInfo]
+
+proc selectRandomServicePeer*(
+ pm: PeerManager, actualPeer: Option[RemotePeerInfo], codec: string
+): Result[RemotePeerInfo, void] =
+ if actualPeer.isSome():
+ alreadyUsedServicePeers.add(actualPeer.get())
+
+ let supportivePeers = pm.switch.peerStore.getPeersByProtocol(codec).filterIt(
+ it notin alreadyUsedServicePeers
+ )
+ if supportivePeers.len == 0:
+ return err()
+
+ let rndPeerIndex = rand(0 .. supportivePeers.len - 1)
+ return ok(supportivePeers[rndPeerIndex])
+
+proc maintainSubscription(
+ wakuNode: WakuNode,
+ filterPubsubTopic: PubsubTopic,
+ filterContentTopic: ContentTopic,
+ filterPeer: RemotePeerInfo,
+ preventPeerSwitch: bool,
+) {.async.} =
+ var actualFilterPeer = filterPeer
+ const maxFailedSubscribes = 3
+ const maxFailedServiceNodeSwitches = 10
+ var noFailedSubscribes = 0
+ var noFailedServiceNodeSwitches = 0
+ while true:
+ info "maintaining subscription at", peer = constructMultiaddrStr(actualFilterPeer)
+ # First use filter-ping to check if we have an active subscription
+ let pingRes = await wakuNode.wakuFilterClient.ping(actualFilterPeer)
+ if pingRes.isErr():
+ # No subscription found. Let's subscribe.
+ error "ping failed.", err = pingRes.error
+ trace "no subscription found. Sending subscribe request"
+
+ let subscribeRes = await wakuNode.filterSubscribe(
+ some(filterPubsubTopic), filterContentTopic, actualFilterPeer
+ )
+
+ if subscribeRes.isErr():
+ noFailedSubscribes += 1
+ error "Subscribe request failed.",
+ err = subscribeRes.error,
+ peer = actualFilterPeer,
+ failCount = noFailedSubscribes
+
+ # TODO: disconnet from failed actualFilterPeer
+ # asyncSpawn(wakuNode.peerManager.switch.disconnect(p))
+ # wakunode.peerManager.peerStore.delete(actualFilterPeer)
+
+ if noFailedSubscribes < maxFailedSubscribes:
+ await sleepAsync(2000) # Wait a bit before retrying
+ continue
+ elif not preventPeerSwitch:
+ let peerOpt = selectRandomServicePeer(
+ wakuNode.peerManager, some(actualFilterPeer), WakuFilterSubscribeCodec
+ )
+ if peerOpt.isOk():
+ actualFilterPeer = peerOpt.get()
+
+ info "Found new peer for codec",
+ codec = filterPubsubTopic, peer = constructMultiaddrStr(actualFilterPeer)
+
+ noFailedSubscribes = 0
+ continue # try again with new peer without delay
+ else:
+ error "Failed to find new service peer. Exiting."
+ noFailedServiceNodeSwitches += 1
+ break
+ else:
+ if noFailedSubscribes > 0:
+ noFailedSubscribes -= 1
+
+ notice "subscribe request successful."
+ else:
+ info "subscription is live."
+
+ await sleepAsync(30000) # Subscription maintenance interval
+
+proc processMixNodes(localnode: WakuNode, nodes: seq[string]) {.async.} =
+ if nodes.len == 0:
+ return
+
+ info "Processing mix nodes: ", nodes = $nodes
+ for node in nodes:
+ var enrRec: enr.Record
+ if enrRec.fromURI(node):
+ let peerInfo = enrRec.toRemotePeerInfo().valueOr:
+ error "Failed to parse mix node", error = error
+ continue
+ localnode.peermanager.addPeer(peerInfo, Discv5)
+ info "Added mix node", peer = peerInfo
+ else:
+ error "Failed to parse mix node ENR", node = node
+
+{.pop.}
+ # @TODO confutils.nim(775, 17) Error: can raise an unlisted exception: ref IOError
+proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
+ let
+ transp = fromPipe(rfd)
+ conf = Chat2Conf.load()
+ nodekey =
+ if conf.nodekey.isSome():
+ conf.nodekey.get()
+ else:
+ PrivateKey.random(Secp256k1, rng[]).tryGet()
+
+ # set log level
+ if conf.logLevel != LogLevel.NONE:
+ setLogLevel(conf.logLevel)
+
+ let natRes = setupNat(
+ conf.nat,
+ clientId,
+ Port(uint16(conf.tcpPort) + conf.portsShift),
+ Port(uint16(conf.udpPort) + conf.portsShift),
+ )
+
+ if natRes.isErr():
+ raise newException(ValueError, "setupNat error " & natRes.error)
+
+ let (extIp, extTcpPort, extUdpPort) = natRes.get()
+
+ var enrBuilder = EnrBuilder.init(nodeKey)
+
+ enrBuilder.withWakuRelaySharding(
+ RelayShards(clusterId: conf.clusterId, shardIds: conf.shards)
+ ).isOkOr:
+ error "failed to add sharded topics to ENR", error = error
+ quit(QuitFailure)
+
+ let recordRes = enrBuilder.build()
+ let record =
+ if recordRes.isErr():
+ error "failed to create enr record", error = recordRes.error
+ quit(QuitFailure)
+ else:
+ recordRes.get()
+
+ let node = block:
+ var builder = WakuNodeBuilder.init()
+ builder.withNodeKey(nodeKey)
+ builder.withRecord(record)
+
+ builder
+ .withNetworkConfigurationDetails(
+ conf.listenAddress,
+ Port(uint16(conf.tcpPort) + conf.portsShift),
+ extIp,
+ extTcpPort,
+ wsBindPort = Port(uint16(conf.websocketPort) + conf.portsShift),
+ wsEnabled = conf.websocketSupport,
+ wssEnabled = conf.websocketSecureSupport,
+ )
+ .tryGet()
+ builder.build().tryGet()
+
+ node.mountAutoSharding(conf.clusterId, conf.numShardsInNetwork).isOkOr:
+ error "failed to mount waku sharding: ", error = error
+ quit(QuitFailure)
+ node.mountMetadata(conf.clusterId, conf.shards).isOkOr:
+ error "failed to mount waku metadata protocol: ", err = error
+ quit(QuitFailure)
+
+ let (mixPrivKey, mixPubKey) = generateKeyPair().valueOr:
+ error "failed to generate mix key pair", error = error
+ return
+
+ (await node.mountMix(conf.clusterId, mixPrivKey)).isOkOr:
+ error "failed to mount waku mix protocol: ", error = $error
+ quit(QuitFailure)
+ if conf.mixnodes.len > 0:
+ await processMixNodes(node, conf.mixnodes)
+ await node.start()
+
+ node.peerManager.start()
+
+ await node.mountLibp2pPing()
+ await node.mountPeerExchangeClient()
+ let pubsubTopic = conf.getPubsubTopic(node, conf.contentTopic)
+ echo "pubsub topic is: " & pubsubTopic
+ let nick = await readNick(transp)
+ echo "Welcome, " & nick & "!"
+
+ var chat = Chat(
+ node: node,
+ transp: transp,
+ subscribed: true,
+ connected: false,
+ started: true,
+ nick: nick,
+ prompt: false,
+ contentTopic: conf.contentTopic,
+ conf: conf,
+ )
+
+ var dnsDiscoveryUrl = none(string)
+
+ if conf.fleet != Fleet.none:
+ # Use DNS discovery to connect to selected fleet
+ echo "Connecting to " & $conf.fleet & " fleet using DNS discovery..."
+
+ if conf.fleet == Fleet.test:
+ dnsDiscoveryUrl = some(
+ "enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im"
+ )
+ else:
+ # Connect to sandbox by default
+ dnsDiscoveryUrl = some(
+ "enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im"
+ )
+ elif conf.dnsDiscoveryUrl != "":
+ # No pre-selected fleet. Discover nodes via DNS using user config
+ debug "Discovering nodes using Waku DNS discovery", url = conf.dnsDiscoveryUrl
+ dnsDiscoveryUrl = some(conf.dnsDiscoveryUrl)
+
+ var discoveredNodes: seq[RemotePeerInfo]
+
+ if dnsDiscoveryUrl.isSome:
+ var nameServers: seq[TransportAddress]
+ for ip in conf.dnsDiscoveryNameServers:
+ nameServers.add(initTAddress(ip, Port(53))) # Assume all servers use port 53
+
+ let dnsResolver = DnsResolver.new(nameServers)
+
+ proc resolver(domain: string): Future[string] {.async, gcsafe.} =
+ trace "resolving", domain = domain
+ let resolved = await dnsResolver.resolveTxt(domain)
+ return resolved[0] # Use only first answer
+
+ var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl.get(), resolver)
+ if wakuDnsDiscovery.isOk:
+ let discoveredPeers = await wakuDnsDiscovery.get().findPeers()
+ if discoveredPeers.isOk:
+ info "Connecting to discovered peers"
+ discoveredNodes = discoveredPeers.get()
+ echo "Discovered and connecting to " & $discoveredNodes
+ waitFor chat.node.connectToNodes(discoveredNodes)
+ else:
+ warn "Failed to init Waku DNS discovery"
+
+ let peerInfo = node.switch.peerInfo
+ let listenStr = $peerInfo.addrs[0] & "/p2p/" & $peerInfo.peerId
+ echo &"Listening on\n {listenStr}"
+
+ if (conf.storenode != "") or (conf.store == true):
+ await node.mountStore()
+
+ var storenode: Option[RemotePeerInfo]
+
+ if conf.storenode != "":
+ let peerInfo = parsePeerInfo(conf.storenode)
+ if peerInfo.isOk():
+ storenode = some(peerInfo.value)
+ else:
+ error "Incorrect conf.storenode", error = peerInfo.error
+ elif discoveredNodes.len > 0:
+ echo "Store enabled, but no store nodes configured. Choosing one at random from discovered peers"
+ storenode = some(discoveredNodes[rand(0 .. len(discoveredNodes) - 1)])
+
+ if storenode.isSome():
+ # We have a viable storenode. Let's query it for historical messages.
+ echo "Connecting to storenode: " & $(storenode.get())
+
+ node.mountStoreClient()
+ node.peerManager.addServicePeer(storenode.get(), WakuStoreCodec)
+
+ proc storeHandler(response: StoreQueryResponse) {.gcsafe.} =
+ for msg in response.messages:
+ let payload =
+ if msg.message.isSome():
+ msg.message.get().payload
+ else:
+ newSeq[byte](0)
+
+ let
+ pb = Chat2Message.init(payload)
+ chatLine =
+ if pb.isOk:
+ pb[].toString()
+ else:
+ string.fromBytes(payload)
+ echo &"{chatLine}"
+ info "Hit store handler"
+
+ let queryRes = await node.query(
+ StoreQueryRequest(contentTopics: @[chat.contentTopic]), storenode.get()
+ )
+ if queryRes.isOk():
+ storeHandler(queryRes.value)
+
+ if conf.edgemode: #Mount light protocol clients
+ node.mountLightPushClient()
+ await node.mountFilterClient()
+ let filterHandler = proc(
+ pubsubTopic: PubsubTopic, msg: WakuMessage
+ ): Future[void] {.async, closure.} =
+ trace "Hit filter handler", contentTopic = msg.contentTopic
+ chat.printReceivedMessage(msg)
+
+ node.wakuFilterClient.registerPushHandler(filterHandler)
+ var servicePeerInfo: RemotePeerInfo
+ if conf.serviceNode != "":
+ servicePeerInfo = parsePeerInfo(conf.serviceNode).valueOr:
+ error "Couldn't parse conf.serviceNode", error = error
+ RemotePeerInfo()
+ if $servicePeerInfo.peerId == "":
+ # Assuming that service node supports all services
+ servicePeerInfo = selectRandomServicePeer(
+ node.peerManager, none(RemotePeerInfo), WakuLightpushCodec
+ ).valueOr:
+ error "Couldn't find any service peer"
+ quit(QuitFailure)
+
+ #await mountLegacyLightPush(node)
+ node.peerManager.addServicePeer(servicePeerInfo, WakuLightpushCodec)
+ node.peerManager.addServicePeer(servicePeerInfo, WakuPeerExchangeCodec)
+
+ # Start maintaining subscription
+ asyncSpawn maintainSubscription(
+ node, pubsubTopic, conf.contentTopic, servicePeerInfo, false
+ )
+ echo "waiting for mix nodes to be discovered..."
+ while true:
+ if node.getMixNodePoolSize() >= 3:
+ break
+ discard await node.fetchPeerExchangePeers()
+ await sleepAsync(1000)
+
+ while node.getMixNodePoolSize() < 3:
+ info "waiting for mix nodes to be discovered",
+ currentpoolSize = node.getMixNodePoolSize()
+ await sleepAsync(1000)
+ notice "ready to publish with mix node pool size ",
+ currentpoolSize = node.getMixNodePoolSize()
+ echo "ready to publish messages now"
+
+ # Once min mixnodes are discovered loop as per default setting
+ node.startPeerExchangeLoop()
+
+ if conf.metricsLogging:
+ startMetricsLog()
+
+ if conf.metricsServer:
+ let metricsServer = startMetricsServer(
+ conf.metricsServerAddress, Port(conf.metricsServerPort + conf.portsShift)
+ )
+
+ await chat.readWriteLoop()
+
+ runForever()
+
+proc main(rng: ref HmacDrbgContext) {.async.} =
+ let (rfd, wfd) = createAsyncPipe()
+ if rfd == asyncInvalidPipe or wfd == asyncInvalidPipe:
+ raise newException(ValueError, "Could not initialize pipe!")
+
+ var thread: Thread[AsyncFD]
+ thread.createThread(readInput, wfd)
+ try:
+ await processInput(rfd, rng)
+ # Handle only ConfigurationError for now
+ # TODO: Throw other errors from the mounting procedure
+ except ConfigurationError as e:
+ raise e
+
+when isMainModule: # isMainModule = true when the module is compiled as the main file
+ let rng = crypto.newRng()
+ try:
+ waitFor(main(rng))
+ except CatchableError as e:
+ raise e
+
+## Dump of things that can be improved:
+##
+## - Incoming dialed peer does not change connected state (not relying on it for now)
+## - Unclear if staticnode argument works (can enter manually)
+## - Don't trigger self / double publish own messages
+## - Test/default to cluster node connection (diff protocol version)
+## - Redirect logs to separate file
+## - Expose basic publish/subscribe etc commands with /syntax
+## - Show part of peerid to know who sent message
+## - Deal with protobuf messages (e.g. other chat protocol, or encrypted)
diff --git a/third-party/nwaku/apps/chat2mix/config_chat2mix.nim b/third-party/nwaku/apps/chat2mix/config_chat2mix.nim
new file mode 100644
index 0000000..1d28149
--- /dev/null
+++ b/third-party/nwaku/apps/chat2mix/config_chat2mix.nim
@@ -0,0 +1,293 @@
+import chronicles, chronos, std/strutils, regex
+
+import
+ eth/keys,
+ libp2p/crypto/crypto,
+ libp2p/crypto/secp,
+ nimcrypto/utils,
+ confutils,
+ confutils/defs,
+ confutils/std/net
+
+import waku/waku_core
+
+type
+ Fleet* = enum
+ none
+ sandbox
+ test
+
+ EthRpcUrl* = distinct string
+
+ Chat2Conf* = object ## General node config
+ edgemode* {.
+ defaultValue: true, desc: "Run the app in edge mode", name: "edge-mode"
+ .}: bool
+
+ logLevel* {.
+ desc: "Sets the log level.", defaultValue: LogLevel.INFO, name: "log-level"
+ .}: LogLevel
+
+ nodekey* {.desc: "P2P node private key as 64 char hex string.", name: "nodekey".}:
+ Option[crypto.PrivateKey]
+
+ listenAddress* {.
+ defaultValue: defaultListenAddress(config),
+ desc: "Listening address for the LibP2P traffic.",
+ name: "listen-address"
+ .}: IpAddress
+
+ tcpPort* {.desc: "TCP listening port.", defaultValue: 60000, name: "tcp-port".}:
+ Port
+
+ udpPort* {.desc: "UDP listening port.", defaultValue: 60000, name: "udp-port".}:
+ Port
+
+ portsShift* {.
+ desc: "Add a shift to all port numbers.", defaultValue: 0, name: "ports-shift"
+ .}: uint16
+
+ nat* {.
+ desc:
+ "Specify method to use for determining public address. " &
+ "Must be one of: any, none, upnp, pmp, extip:.",
+ defaultValue: "any"
+ .}: string
+
+ ## Persistence config
+ dbPath* {.
+ desc: "The database path for peristent storage", defaultValue: "", name: "db-path"
+ .}: string
+
+ persistPeers* {.
+ desc: "Enable peer persistence: true|false",
+ defaultValue: false,
+ name: "persist-peers"
+ .}: bool
+
+ persistMessages* {.
+ desc: "Enable message persistence: true|false",
+ defaultValue: false,
+ name: "persist-messages"
+ .}: bool
+
+ ## Relay config
+ relay* {.
+ desc: "Enable relay protocol: true|false", defaultValue: true, name: "relay"
+ .}: bool
+
+ staticnodes* {.
+ desc: "Peer multiaddr to directly connect with. Argument may be repeated.",
+ name: "staticnode",
+ defaultValue: @[]
+ .}: seq[string]
+
+ mixnodes* {.
+ desc: "Peer ENR to add as a mixnode. Argument may be repeated.", name: "mixnode"
+ .}: seq[string]
+
+ keepAlive* {.
+ desc: "Enable keep-alive for idle connections: true|false",
+ defaultValue: false,
+ name: "keep-alive"
+ .}: bool
+
+ clusterId* {.
+ desc:
+ "Cluster id that the node is running in. Node in a different cluster id is disconnected.",
+ defaultValue: 1,
+ name: "cluster-id"
+ .}: uint16
+
+ numShardsInNetwork* {.
+ desc: "Number of shards in the network",
+ defaultValue: 8,
+ name: "num-shards-in-network"
+ .}: uint32
+
+ shards* {.
+ desc:
+ "Shards index to subscribe to [0..NUM_SHARDS_IN_NETWORK-1]. Argument may be repeated.",
+ defaultValue:
+ @[
+ uint16(0),
+ uint16(1),
+ uint16(2),
+ uint16(3),
+ uint16(4),
+ uint16(5),
+ uint16(6),
+ uint16(7),
+ ],
+ name: "shard"
+ .}: seq[uint16]
+
+ ## Store config
+ store* {.
+ desc: "Enable store protocol: true|false", defaultValue: false, name: "store"
+ .}: bool
+
+ storenode* {.
+ desc: "Peer multiaddr to query for storage.", defaultValue: "", name: "storenode"
+ .}: string
+
+ ## Filter config
+ filter* {.
+ desc: "Enable filter protocol: true|false", defaultValue: false, name: "filter"
+ .}: bool
+
+ ## Lightpush config
+ lightpush* {.
+ desc: "Enable lightpush protocol: true|false",
+ defaultValue: false,
+ name: "lightpush"
+ .}: bool
+
+ servicenode* {.
+ desc: "Peer multiaddr to request lightpush and filter services",
+ defaultValue: "",
+ name: "servicenode"
+ .}: string
+
+ ## Metrics config
+ metricsServer* {.
+ desc: "Enable the metrics server: true|false",
+ defaultValue: false,
+ name: "metrics-server"
+ .}: bool
+
+ metricsServerAddress* {.
+ desc: "Listening address of the metrics server.",
+ defaultValue: parseIpAddress("127.0.0.1"),
+ name: "metrics-server-address"
+ .}: IpAddress
+
+ metricsServerPort* {.
+ desc: "Listening HTTP port of the metrics server.",
+ defaultValue: 8008,
+ name: "metrics-server-port"
+ .}: uint16
+
+ metricsLogging* {.
+ desc: "Enable metrics logging: true|false",
+ defaultValue: true,
+ name: "metrics-logging"
+ .}: bool
+
+ ## DNS discovery config
+ dnsDiscovery* {.
+ desc:
+ "Deprecated, please set dns-discovery-url instead. Enable discovering nodes via DNS",
+ defaultValue: false,
+ name: "dns-discovery"
+ .}: bool
+
+ dnsDiscoveryUrl* {.
+ desc: "URL for DNS node list in format 'enrtree://@'",
+ defaultValue: "",
+ name: "dns-discovery-url"
+ .}: string
+
+ dnsDiscoveryNameServers* {.
+ desc: "DNS name server IPs to query. Argument may be repeated.",
+ defaultValue: @[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")],
+ name: "dns-discovery-name-server"
+ .}: seq[IpAddress]
+
+ ## Chat2 configuration
+ fleet* {.
+ desc:
+ "Select the fleet to connect to. This sets the DNS discovery URL to the selected fleet.",
+ defaultValue: Fleet.test,
+ name: "fleet"
+ .}: Fleet
+
+ contentTopic* {.
+ desc: "Content topic for chat messages.",
+ defaultValue: "/toy-chat-mix/2/huilong/proto",
+ name: "content-topic"
+ .}: string
+
+ ## Websocket Configuration
+ websocketSupport* {.
+ desc: "Enable websocket: true|false",
+ defaultValue: false,
+ name: "websocket-support"
+ .}: bool
+
+ websocketPort* {.
+ desc: "WebSocket listening port.", defaultValue: 8000, name: "websocket-port"
+ .}: Port
+
+ websocketSecureSupport* {.
+ desc: "WebSocket Secure Support.",
+ defaultValue: false,
+ name: "websocket-secure-support"
+ .}: bool ## rln-relay configuration
+
+# NOTE: Keys are different in nim-libp2p
+proc parseCmdArg*(T: type crypto.PrivateKey, p: string): T =
+ try:
+ let key = SkPrivateKey.init(utils.fromHex(p)).tryGet()
+ # XXX: Here at the moment
+ result = crypto.PrivateKey(scheme: Secp256k1, skkey: key)
+ except CatchableError as e:
+ raise newException(ValueError, "Invalid private key")
+
+proc completeCmdArg*(T: type crypto.PrivateKey, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type IpAddress, p: string): T =
+ try:
+ result = parseIpAddress(p)
+ except CatchableError as e:
+ raise newException(ValueError, "Invalid IP address")
+
+proc completeCmdArg*(T: type IpAddress, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type Port, p: string): T =
+ try:
+ result = Port(parseInt(p))
+ except CatchableError as e:
+ raise newException(ValueError, "Invalid Port number")
+
+proc completeCmdArg*(T: type Port, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type Option[uint], p: string): T =
+ try:
+ some(parseUint(p))
+ except CatchableError:
+ raise newException(ValueError, "Invalid unsigned integer")
+
+proc completeCmdArg*(T: type EthRpcUrl, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type EthRpcUrl, s: string): T =
+ ## allowed patterns:
+ ## http://url:port
+ ## https://url:port
+ ## http://url:port/path
+ ## https://url:port/path
+ ## http://url/with/path
+ ## http://url:port/path?query
+ ## https://url:port/path?query
+ ## disallowed patterns:
+ ## any valid/invalid ws or wss url
+ var httpPattern =
+ re2"^(https?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*"
+ var wsPattern =
+ re2"^(wss?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*"
+ if regex.match(s, wsPattern):
+ raise newException(
+ ValueError, "Websocket RPC URL is not supported, Please use an HTTP URL"
+ )
+ if not regex.match(s, httpPattern):
+ raise newException(ValueError, "Invalid HTTP RPC URL")
+ return EthRpcUrl(s)
+
+func defaultListenAddress*(conf: Chat2Conf): IpAddress =
+ # TODO: How should we select between IPv4 and IPv6
+ # Maybe there should be a config option for this.
+ (static parseIpAddress("0.0.0.0"))
diff --git a/third-party/nwaku/apps/chat2mix/nim.cfg b/third-party/nwaku/apps/chat2mix/nim.cfg
new file mode 100644
index 0000000..2231f2e
--- /dev/null
+++ b/third-party/nwaku/apps/chat2mix/nim.cfg
@@ -0,0 +1,4 @@
+-d:chronicles_line_numbers
+-d:chronicles_runtime_filtering:on
+-d:discv5_protocol_id:d5waku
+path = "../.."
diff --git a/third-party/nwaku/apps/liteprotocoltester/.env b/third-party/nwaku/apps/liteprotocoltester/.env
new file mode 100644
index 0000000..0330284
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/.env
@@ -0,0 +1,27 @@
+START_PUBLISHING_AFTER_SECS=45
+# can add some seconds delay before SENDER starts publishing
+
+NUM_MESSAGES=0
+# 0 for infinite number of messages
+
+MESSAGE_INTERVAL_MILLIS=8000
+# ms delay between messages
+
+
+MIN_MESSAGE_SIZE=15Kb
+MAX_MESSAGE_SIZE=145Kb
+
+## for wakusim
+#SHARD=0
+#CONTENT_TOPIC=/tester/2/light-pubsub-test/wakusim
+#CLUSTER_ID=66
+
+## for status.prod
+#SHARDS=32
+CONTENT_TOPIC=/tester/2/light-pubsub-test/fleet
+CLUSTER_ID=16
+
+## for TWN
+#SHARD=4
+#CONTENT_TOPIC=/tester/2/light-pubsub-test/twn
+#CLUSTER_ID=1
diff --git a/third-party/nwaku/apps/liteprotocoltester/Dockerfile.liteprotocoltester b/third-party/nwaku/apps/liteprotocoltester/Dockerfile.liteprotocoltester
new file mode 100644
index 0000000..1948300
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/Dockerfile.liteprotocoltester
@@ -0,0 +1,37 @@
+ # TESTING IMAGE --------------------------------------------------------------
+
+ ## NOTICE: This is a short cut build file for ubuntu users who compiles nwaku in ubuntu distro.
+ ## This is used for faster turnaround time for testing the compiled binary.
+ ## Prerequisites: compiled liteprotocoltester binary in build/ directory
+
+ FROM ubuntu:noble AS prod
+
+ LABEL maintainer="zoltan@status.im"
+ LABEL source="https://github.com/waku-org/nwaku"
+ LABEL description="Lite Protocol Tester: Waku light-client"
+ LABEL commit="unknown"
+ LABEL version="unknown"
+
+ # DevP2P, LibP2P, and JSON RPC ports
+ EXPOSE 30303 60000 8545
+
+ # Referenced in the binary
+ RUN apt-get update && apt-get install -y --no-install-recommends \
+ libgcc1 \
+ libpcre3 \
+ libpq-dev \
+ wget \
+ iproute2 \
+ && rm -rf /var/lib/apt/lists/*
+
+ # Fix for 'Error loading shared library libpcre.so.3: No such file or directory'
+ RUN ln -s /usr/lib/libpcre.so /usr/lib/libpcre.so.3
+
+ COPY build/liteprotocoltester /usr/bin/
+ COPY apps/liteprotocoltester/run_tester_node.sh /usr/bin/
+ COPY apps/liteprotocoltester/run_tester_node_on_fleet.sh /usr/bin/
+
+ ENTRYPOINT ["/usr/bin/run_tester_node.sh", "/usr/bin/liteprotocoltester"]
+
+ # # By default just show help if called without arguments
+ CMD ["--help"]
diff --git a/third-party/nwaku/apps/liteprotocoltester/Dockerfile.liteprotocoltester.compile b/third-party/nwaku/apps/liteprotocoltester/Dockerfile.liteprotocoltester.compile
new file mode 100644
index 0000000..497570c
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/Dockerfile.liteprotocoltester.compile
@@ -0,0 +1,76 @@
+# BUILD NIM APP ----------------------------------------------------------------
+FROM rust:1.77.1-alpine3.18 AS nim-build
+
+ARG NIMFLAGS
+ARG MAKE_TARGET=liteprotocoltester
+ARG NIM_COMMIT
+ARG LOG_LEVEL=TRACE
+
+# Get build tools and required header files
+RUN apk add --no-cache bash git build-base openssl-dev linux-headers curl jq
+
+WORKDIR /app
+COPY . .
+
+# workaround for alpine issue: https://github.com/alpinelinux/docker-alpine/issues/383
+RUN apk update && apk upgrade
+
+# Ran separately from 'make' to avoid re-doing
+RUN git submodule update --init --recursive
+
+# Slowest build step for the sake of caching layers
+RUN make -j$(nproc) deps QUICK_AND_DIRTY_COMPILER=1 ${NIM_COMMIT}
+
+# Build the final node binary
+RUN make -j$(nproc) ${NIM_COMMIT} $MAKE_TARGET LOG_LEVEL=${LOG_LEVEL} NIMFLAGS="${NIMFLAGS}"
+
+
+# REFERENCE IMAGE as BASE for specialized PRODUCTION IMAGES----------------------------------------
+FROM alpine:3.18 AS base_lpt
+
+ARG MAKE_TARGET=liteprotocoltester
+
+LABEL maintainer="zoltan@status.im"
+LABEL source="https://github.com/waku-org/nwaku"
+LABEL description="Lite Protocol Tester: Waku light-client"
+LABEL commit="unknown"
+LABEL version="unknown"
+
+# DevP2P, LibP2P, and JSON RPC ports
+EXPOSE 30303 60000 8545
+
+# Referenced in the binary
+RUN apk add --no-cache libgcc libpq-dev \
+ wget \
+ iproute2 \
+ python3
+
+# Fix for 'Error loading shared library libpcre.so.3: No such file or directory'
+RUN ln -s /usr/lib/libpcre.so /usr/lib/libpcre.so.3
+
+COPY --from=nim-build /app/build/liteprotocoltester /usr/bin/
+RUN chmod +x /usr/bin/liteprotocoltester
+
+# Standalone image to be used manually and in lpt-runner -------------------------------------------
+FROM base_lpt AS standalone_lpt
+
+COPY --from=nim-build /app/apps/liteprotocoltester/run_tester_node.sh /usr/bin/
+COPY --from=nim-build /app/apps/liteprotocoltester/run_tester_node_on_fleet.sh /usr/bin/
+
+RUN chmod +x /usr/bin/run_tester_node.sh
+
+ENTRYPOINT ["/usr/bin/run_tester_node.sh", "/usr/bin/liteprotocoltester"]
+
+# Image for infra deployment -------------------------------------------
+FROM base_lpt AS deployment_lpt
+
+# let supervisor python script flush logs immediately
+ENV PYTHONUNBUFFERED="1"
+
+COPY --from=nim-build /app/apps/liteprotocoltester/run_tester_node_at_infra.sh /usr/bin/
+COPY --from=nim-build /app/apps/liteprotocoltester/infra.env /usr/bin/
+COPY --from=nim-build /app/apps/liteprotocoltester/lpt_supervisor.py /usr/bin/
+RUN chmod +x /usr/bin/run_tester_node_at_infra.sh
+RUN chmod +x /usr/bin/lpt_supervisor.py
+
+ENTRYPOINT ["/usr/bin/lpt_supervisor.py"]
diff --git a/third-party/nwaku/apps/liteprotocoltester/README.md b/third-party/nwaku/apps/liteprotocoltester/README.md
new file mode 100644
index 0000000..ea02ec1
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/README.md
@@ -0,0 +1,329 @@
+# Waku - Lite Protocol Tester
+
+## Aim
+
+Testing reliability of light client protocols in different scale.
+Measure message delivery reliability and latency between light push client(s) and a filter client(s) node(s).
+
+## Concept of testing
+
+A tester node is configured either 'publisher' or 'receiver' and connects to a certain service node.
+All service protocols are disabled except for lightpush client or filter client. This way we would like to simulate
+a light client application.
+Each publisher pumps messages to the network in a preconfigured way (number of messages, frequency) while on the receiver side
+we would like to track and measure message losses, mis-ordered receives, late arrived messages and latencies.
+Ideally the tester nodes will connect to different edge of the network where we can gather more result from mulitple publishers
+and multiple receivers.
+
+Publishers are fill all message payloads with information about the test message and sender, helping the receiver side to calculate results.
+
+## Usage
+
+### Using lpt-runner
+
+For ease of use, you can clone lpt-runner repository. That will utilize previously pushed liteprotocoltester docker image.
+It is recommended to use this method for fleet testing.
+
+```bash
+git clone https://github.com/waku-org/lpt-runner.git
+cd lpt-runner
+
+# check Reame.md for more information
+# edit .env file to your needs
+
+docker compose up -d
+
+# navigate localhost:3033 to see the lite-protocol-tester dashboard
+```
+
+> See more detailed examples below.
+
+### Integration with waku-simulator!
+
+- For convenience, integration is done in cooperation with waku-simulator repository, but nothing is tightly coupled.
+- waku-simulator must be started separately with its own configuration.
+- To enable waku-simulator working without RLN currently a separate branch is to be used.
+- When waku-simulator is configured and up and running, lite-protocol-tester composite docker setup can be started.
+
+```bash
+
+# Start waku-simulator
+
+git clone https://github.com/waku-org/waku-simulator.git ../waku-simulator
+cd ../waku-simulator
+git checkout chore-integrate-liteprotocoltester
+
+# optionally edit .env file
+
+docker compose -f docker-compose-norln.yml up -d
+
+# navigate localhost:30001 to see the waku-simulator dashboard
+
+cd ../{your-repository}
+
+make LOG_LEVEL=DEBUG liteprotocoltester
+
+cd apps/liteprotocoltester
+
+# optionally edit .env file
+
+docker compose -f docker-compose-on-simularor.yml build
+docker compose -f docker-compose-on-simularor.yml up -d
+docker compose -f docker-compose-on-simularor.yml logs -f receivernode
+```
+#### Current setup
+
+- waku-simulator is configured to run with 25 full node
+- liteprotocoltester is configured to run with 3 publisher and 1 receiver
+- liteprotocoltester is configured to run 1 lightpush service and a filter service node
+ - light clients are connected accordingly
+- publishers will send 250 messages in every 200ms with size between 1KiB and 120KiB
+- Notice there is a configurable wait before start publishing messages as it is noticed time is needed for the service nodes to get connected to full nodes from simulator
+- light clients will print report on their and the connected service node's connectivity to the network in every 20 secs.
+
+#### Test monitoring
+
+Navigate to http://localhost:3033 to see the lite-protocol-tester dashboard.
+
+### Run independently on a chosen waku fleet
+
+This option is simple as is just to run the built liteprotocoltester binary with run_tester_node.sh script.
+
+Syntax:
+`./run_tester_node.sh `
+
+How to run from you nwaku repository:
+```bash
+cd ../{your-repository}
+
+make LOG_LEVEL=DEBUG liteprotocoltester
+
+cd apps/liteprotocoltester
+
+# optionally edit .env file
+
+# run publisher side
+./run_tester_node.sh ../../build/liteprotocoltester SENDER [chosen service node address that support lightpush]
+
+# or run receiver side
+./run_tester_node.sh ../../build/liteprotocoltester RECEIVER [chosen service node address that support filter service]
+```
+
+#### Recommendations
+
+In order to run on any kind of network, it is recommended to deploy the built `liteprotocoltester` binary with the `.env` file and the `run_tester_node.sh` script to the desired machine.
+
+Select a lightpush service node and a filter service node from the targeted network, or you can run your own. Note down the selected peers peer_id.
+
+Run a SENDER role liteprotocoltester and a RECEIVER role one on different terminals. Depending on the test aim, you may want to redirect the output to a file.
+
+> RECEIVER side will periodically print statistics to standard output.
+
+## Configuration
+
+### Environment variables for docker compose runs
+
+| Variable | Description | Default |
+| ---: | :--- | :--- |
+| NUM_MESSAGES | Number of message to publish, 0 means infinite | 120 |
+| MESSAGE_INTERVAL_MILLIS | Frequency of messages in milliseconds | 1000 |
+| SHARD | Used shard for testing | 0 |
+| CONTENT_TOPIC | content_topic for testing | /tester/1/light-pubsub-example/proto |
+| CLUSTER_ID | cluster_id of the network | 16 |
+| START_PUBLISHING_AFTER_SECS | Delay in seconds before starting to publish to let service node connected | 5 |
+| MIN_MESSAGE_SIZE | Minimum message size in bytes | 1KiB |
+| MAX_MESSAGE_SIZE | Maximum message size in bytes | 120KiB |
+
+
+### Lite Protocol Tester application cli options
+
+| Option | Description | Default |
+| :--- | :--- | :--- |
+| --test_func | separation of PUBLISHER or RECEIVER mode | RECEIVER |
+| --service-node| Address of the service node to use for lightpush and/or filter service | - |
+| --bootstrap-node| Address of the fleet's bootstrap node to use to determine service peer randomly choosen from the network. `--service-node` switch has precedence over this | - |
+| --num-messages | Number of message to publish | 120 |
+| --message-interval | Frequency of messages in milliseconds | 1000 |
+| --min-message-size | Minimum message size in bytes | 1KiB |
+| --max-message-size | Maximum message size in bytes | 120KiB |
+| --start-publishing-after | Delay in seconds before starting to publish to let service node connected in seconds | 5 |
+| --pubsub-topic | Used pubsub_topic for testing | /waku/2/default-waku/proto |
+| --content_topic | content_topic for testing | /tester/1/light-pubsub-example/proto |
+| --cluster-id | Cluster id for the test | 0 |
+| --config-file | TOML configuration file to fine tune the light waku node
Note that some configurations (full node services) are not taken into account | - |
+| --nat |Same as wakunode "nat" configuration, appear here to ease test setup | any |
+| --rest-address | For convenience rest configuration can be done here | 127.0.0.1 |
+| --rest-port | For convenience rest configuration can be done here | 8654 |
+| --rest-allow-origin | For convenience rest configuration can be done here | * |
+| --log-level | Log level for the application | DEBUG |
+| --log-format | Logging output format (TEXT or JSON) | TEXT |
+| --metrics-port | Metrics scarpe port | 8003 |
+
+### Specifying peer addresses
+
+Service node or bootstrap addresses can be specified in multiadress or ENR form.
+
+### Using bootstrap nodes
+
+There are multiple benefits of using bootstrap nodes. By using them liteprotocoltester will use Peer Exchange protocol to get possible peers from the network that are capable to serve as service peers for testing. Additionally it will test dial them to verify their connectivity - this will be reported in the logs and on dashboard metrics.
+Also by using bootstrap node and peer exchange discovery, litprotocoltester will be able to simulate service peer switch in case of failures. There are built in tresholds count for service peer failures (3) after service peer will be switched during the test. Also there will be max 10 trials of switching peer before test declared failed and quit.
+These service peer failures are reported, thus extending network reliability measures.
+
+### Building docker image
+
+Easiest way to build the docker image is to use the provided Makefile target.
+
+```bash
+cd
+make docker-liteprotocoltester
+```
+This will build liteprotocoltester from the ground up and create a docker image with the binary copied to it under image name and tag `wakuorg/liteprotocoltester:latest`.
+
+#### Building public image
+
+If you want to push the image to a public registry, you can use the jenkins job to do so.
+The job is available at https://ci.status.im/job/waku/job/liteprotocoltester/job/build-liteprotocoltester-image
+
+#### Building and deployment for infra testing
+
+For specific and continuous testing purposes we have a deployment of `liteprotocoltester` test suite to our infra appliances.
+This has its own configuration, constraints and requirements. To ease this job, image shall be built and pushed with `deploy` tag.
+This can be done by the jenkins job mentioned above.
+
+or manually by:
+```bash
+cd
+make DOCKER_LPT_TAG=deploy docker-liteprotocoltester
+```
+
+The image created with this method will be different from under any other tag. It prepared to run a preconfigured test suite continuously.
+It will also miss prometheus metrics scraping endpoint and grafana, thus it is not recommended to use it for general testing.
+
+#### Manually building for docker compose runs on simulator or standalone
+Please note that currently to ease testing and development tester application docker image is based on ubuntu and uses the externally pre-built binary of 'liteprotocoltester'.
+This speeds up image creation. Another dokcer build file is provided for proper build of boundle image.
+
+> `Dockerfile.liteprotocoltester` will create an ubuntu based image with the binary copied from the build directory.
+
+> `Dockerfile.liteprotocoltester.compile` will create an ubuntu based image completely compiled from source. This can be slow.
+
+#### Creating standalone runner docker image
+
+To ease the work with lite-protocol-tester, a docker image is possible to build.
+With that image it is easy to run the application in a container.
+
+> `Dockerfile.liteprotocoltester` will create an ubuntu image with the binary copied from the build directory. You need to pre-build the application.
+
+Here is how to build and run:
+```bash
+cd
+make liteprotocoltester
+
+cd apps/liteprotocoltester
+docker build -t liteprotocoltester:latest -f Dockerfile.liteprotocoltester ../..
+
+# alternatively you can push it to a registry
+
+# edit and adjust .env file to your needs and for the network configuration
+
+docker run --env-file .env liteprotocoltester:latest RECEIVER
+
+docker run --env-file .env liteprotocoltester:latest SENDER
+```
+
+#### Run test with auto service peer selection from a fleet using bootstrap node
+
+```bash
+
+docker run --env-file .env liteprotocoltester:latest RECEIVER BOOTSTRAP
+
+docker run --env-file .env liteprotocoltester:latest SENDER BOOTSTRAP
+```
+
+> Notice that official image is also available at harbor.status.im/wakuorg/liteprotocoltester:latest
+
+## Examples
+
+### Bootstrap or Service node selection
+
+The easiest way to get the proper bootstrap nodes for the tests from https://fleets.status.im page.
+Adjust on which fleets you would like to run the tests.
+
+> Please note that not all of them configured to support Peer Exchange protocol, those ones cannot be for bootstrap nodes for `liteprotocoltester`.
+
+### Environment variables
+You need not necessary to use .env file, although it can be more convenient.
+Anytime you can override all or part of the environment variables defined in the .env file.
+
+### Run standalone
+
+Example of running the liteprotocoltester in standalone mode on status.stagin network.
+Testing includes using bootstrap nodes to gather service peers from the network via Peer Exchange protocol.
+Both parties will test-dial all the peers retrieved with the corresponding protocol.
+Sender will start publishing messages after 60 seconds, sending 200 messages with 1 second delay between them.
+Message size will be between 15KiB and 145KiB.
+Cluster id and Pubsub-topic must be accurately set according to the network configuration.
+
+The example shows that either multiaddress or ENR form accepted.
+
+```bash
+export START_PUBLISHING_AFTER_SECS=60
+export NUM_MESSAGES=200
+export MESSAGE_INTERVAL_MILLIS=1000
+export MIN_MESSAGE_SIZE=15Kb
+export MAX_MESSAGE_SIZE=145Kb
+export SHARD=32
+export CONTENT_TOPIC=/tester/2/light-pubsub-test/fleet
+export CLUSTER_ID=16
+
+docker run harbor.status.im/wakuorg/liteprotocoltester:latest RECEIVER /dns4/boot-01.do-ams3.status.staging.status.im/tcp/30303/p2p/16Uiu2HAmQE7FXQc6iZHdBzYfw3qCSDa9dLc1wsBJKoP4aZvztq2d BOOTSTRAP
+
+# in different terminal session, repeat the exports and run the other party of the test.
+docker run harbor.status.im/wakuorg/liteprotocoltester:latest SENDER enr:-QEiuECJPv2vL00Jp5sTEMAFyW7qXkK2cFgphlU_G8-FJuJqoW_D5aWIy3ylGdv2K8DkiG7PWgng4Ql_VI7Qc2RhBdwfAYJpZIJ2NIJpcIQvTKi6im11bHRpYWRkcnO4cgA2NjFib290LTAxLmFjLWNuLWhvbmdrb25nLWMuc3RhdHVzLnN0YWdpbmcuc3RhdHVzLmltBnZfADg2MWJvb3QtMDEuYWMtY24taG9uZ2tvbmctYy5zdGF0dXMuc3RhZ2luZy5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDkbgV7oqPNmFtX5FzSPi9WH8kkmrPB1R3n9xRXge91M-DdGNwgnZfg3VkcIIjKIV3YWt1Mg0 BOOTSTRAP
+
+```
+
+### Use of lpt-runner
+
+Another method is to use [lpt-runner repository](https://github.com/waku-org/lpt-runner/tree/master).
+This extends testing with grafana dashboard and ease the test setup.
+Please read the corresponding [README](https://github.com/waku-org/lpt-runner/blob/master/README.md) there as well.
+
+In this example we will run similar test as above but there will be 3 instances of publisher nodes and 1 receiver node.
+This test uses waku.sandbox fleet which is connected to TWN. This implies lower message rates due to the RLN rate limation.
+Also leave a gap of 120 seconds before starting to publish messages to let receiver side fully finish peer test-dialing.
+For TWN network it is always wise to use bootstrap nodes with Peer Exchange support.
+
+> Theoritically we can use the same bootstrap nodes for both parties, but it is recommended to use different ones to simulate different network edges, thus getting more meaningful results.
+
+```bash
+git clone https://github.com/waku-org/lpt-runner.git
+cd lpt-runner
+
+export NUM_PUBLISHER_NODES=3
+export NUM_RECEIVER_NODES=1
+export START_PUBLISHING_AFTER_SECS=120
+export NUM_MESSAGES=300
+export MESSAGE_INTERVAL_MILLIS=7000
+export MIN_MESSAGE_SIZE=15Kb
+export MAX_MESSAGE_SIZE=145Kb
+export SHARD=4
+export CONTENT_TOPIC=/tester/2/light-pubsub-test/twn
+export CLUSTER_ID=1
+
+export FILTER_BOOTSTRAP=/dns4/node-01.ac-cn-hongkong-c.waku.sandbox.status.im/tcp/30303/p2p/16Uiu2HAmQYiojgZ8APsh9wqbWNyCstVhnp9gbeNrxSEQnLJchC92
+export LIGHTPUSH_BOOTSTRAP=/dns4/node-01.do-ams3.waku.sandbox.status.im/tcp/30303/p2p/16Uiu2HAmNaeL4p3WEYzC9mgXBmBWSgWjPHRvatZTXnp8Jgv3iKsb
+
+docker compose up -d
+
+# we can check logs from one or all SENDER
+docker compose logs -f --index 1 publishernode
+
+# for checking receiver side performance
+docker compose logs -f receivernode
+
+# when test completed
+docker compose down
+```
+
+For dashboard navigate to http://localhost:3033
diff --git a/third-party/nwaku/apps/liteprotocoltester/diagnose_connections.nim b/third-party/nwaku/apps/liteprotocoltester/diagnose_connections.nim
new file mode 100644
index 0000000..f595b4e
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/diagnose_connections.nim
@@ -0,0 +1,65 @@
+when (NimMajor, NimMinor) < (1, 4):
+ {.push raises: [Defect].}
+else:
+ {.push raises: [].}
+
+import
+ std/[options, net, strformat],
+ chronicles,
+ chronos,
+ metrics,
+ libbacktrace,
+ libp2p/crypto/crypto,
+ confutils,
+ libp2p/wire
+
+import
+ ../../tools/confutils/cli_args,
+ waku/[
+ node/peer_manager,
+ waku_lightpush/common,
+ waku_relay,
+ waku_filter_v2,
+ waku_peer_exchange/protocol,
+ waku_core/multiaddrstr,
+ waku_enr/capabilities,
+ ]
+logScope:
+ topics = "diagnose connections"
+
+proc allPeers(pm: PeerManager): string =
+ var allStr: string = ""
+ for idx, peer in pm.switch.peerStore.peers():
+ allStr.add(
+ " " & $idx & ". | " & constructMultiaddrStr(peer) & " | agent: " &
+ peer.getAgent() & " | protos: " & $peer.protocols & " | caps: " &
+ $peer.enr.map(getCapabilities) & "\n"
+ )
+ return allStr
+
+proc logSelfPeers*(pm: PeerManager) =
+ let selfLighpushPeers = pm.switch.peerStore.getPeersByProtocol(WakuLightPushCodec)
+ let selfRelayPeers = pm.switch.peerStore.getPeersByProtocol(WakuRelayCodec)
+ let selfFilterPeers = pm.switch.peerStore.getPeersByProtocol(WakuFilterSubscribeCodec)
+ let selfPxPeers = pm.switch.peerStore.getPeersByProtocol(WakuPeerExchangeCodec)
+
+ let printable = catch:
+ """*------------------------------------------------------------------------------------------*
+| Self ({constructMultiaddrStr(pm.switch.peerInfo)}) peers:
+*------------------------------------------------------------------------------------------*
+| Lightpush peers({selfLighpushPeers.len()}): ${selfLighpushPeers}
+*------------------------------------------------------------------------------------------*
+| Filter peers({selfFilterPeers.len()}): ${selfFilterPeers}
+*------------------------------------------------------------------------------------------*
+| Relay peers({selfRelayPeers.len()}): ${selfRelayPeers}
+*------------------------------------------------------------------------------------------*
+| PX peers({selfPxPeers.len()}): ${selfPxPeers}
+*------------------------------------------------------------------------------------------*
+| All peers with protocol support:
+{allPeers(pm)}
+*------------------------------------------------------------------------------------------*""".fmt()
+
+ if printable.isErr():
+ echo "Error while printing statistics: " & printable.error().msg
+ else:
+ echo printable.get()
diff --git a/third-party/nwaku/apps/liteprotocoltester/docker-compose-on-simularor.yml b/third-party/nwaku/apps/liteprotocoltester/docker-compose-on-simularor.yml
new file mode 100644
index 0000000..9e899f7
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/docker-compose-on-simularor.yml
@@ -0,0 +1,227 @@
+version: "3.7"
+x-logging: &logging
+ logging:
+ driver: json-file
+ options:
+ max-size: 1000m
+
+# Environment variable definitions
+x-eth-client-address: ð_client_address ${ETH_CLIENT_ADDRESS:-} # Add your ETH_CLIENT_ADDRESS after the "-"
+
+x-rln-environment: &rln_env
+ RLN_RELAY_CONTRACT_ADDRESS: ${RLN_RELAY_CONTRACT_ADDRESS:-0xF471d71E9b1455bBF4b85d475afb9BB0954A29c4}
+ RLN_RELAY_CRED_PATH: ${RLN_RELAY_CRED_PATH:-} # Optional: Add your RLN_RELAY_CRED_PATH after the "-"
+ RLN_RELAY_CRED_PASSWORD: ${RLN_RELAY_CRED_PASSWORD:-} # Optional: Add your RLN_RELAY_CRED_PASSWORD after the "-"
+
+x-test-running-conditions: &test_running_conditions
+ NUM_MESSAGES: ${NUM_MESSAGES:-120}
+ MESSAGE_INTERVAL_MILLIS: "${MESSAGE_INTERVAL_MILLIS:-1000}"
+ SHARD: ${SHARD:-0}
+ CONTENT_TOPIC: ${CONTENT_TOPIC:-/tester/2/light-pubsub-test/wakusim}
+ CLUSTER_ID: ${CLUSTER_ID:-66}
+ MIN_MESSAGE_SIZE: ${MIN_MESSAGE_SIZE:-1Kb}
+ MAX_MESSAGE_SIZE: ${MAX_MESSAGE_SIZE:-150Kb}
+ START_PUBLISHING_AFTER_SECS: ${START_PUBLISHING_AFTER_SECS:-5} # seconds
+
+
+# Services definitions
+services:
+ lightpush-service:
+ image: ${NWAKU_IMAGE:-harbor.status.im/wakuorg/nwaku:latest-release}
+ # ports:
+ # - 30304:30304/tcp
+ # - 30304:30304/udp
+ # - 9005:9005/udp
+ # - 127.0.0.1:8003:8003
+ # - 80:80 #Let's Encrypt
+ # - 8000:8000/tcp #WSS
+ # - 127.0.0.1:8645:8645
+ <<:
+ - *logging
+ environment:
+ DOMAIN: ${DOMAIN}
+ RLN_RELAY_CRED_PASSWORD: "${RLN_RELAY_CRED_PASSWORD}"
+ ETH_CLIENT_ADDRESS: *eth_client_address
+ EXTRA_ARGS: ${EXTRA_ARGS}
+ <<:
+ - *rln_env
+ - *test_running_conditions
+ volumes:
+ - ./run_service_node.sh:/opt/run_service_node.sh:Z
+ - ${CERTS_DIR:-./certs}:/etc/letsencrypt/:Z
+ - ./rln_tree:/etc/rln_tree/:Z
+ - ./keystore:/keystore:Z
+ entrypoint: sh
+ command:
+ - /opt/run_service_node.sh
+ - LIGHTPUSH
+ networks:
+ - waku-simulator_simulation
+
+ publishernode:
+ image: waku.liteprotocoltester:latest
+ build:
+ context: ../..
+ dockerfile: ./apps/liteprotocoltester/Dockerfile.liteprotocoltester
+ deploy:
+ replicas: ${NUM_PUBLISHER_NODES:-3}
+ # ports:
+ # - 30304:30304/tcp
+ # - 30304:30304/udp
+ # - 9005:9005/udp
+ # - 127.0.0.1:8003:8003
+ # - 80:80 #Let's Encrypt
+ # - 8000:8000/tcp #WSS
+ # - 127.0.0.1:8646:8646
+ <<:
+ - *logging
+ environment:
+ DOMAIN: ${DOMAIN}
+ RLN_RELAY_CRED_PASSWORD: "${RLN_RELAY_CRED_PASSWORD}"
+ ETH_CLIENT_ADDRESS: *eth_client_address
+ EXTRA_ARGS: ${EXTRA_ARGS}
+ <<:
+ - *rln_env
+ - *test_running_conditions
+ volumes:
+ - ${CERTS_DIR:-./certs}:/etc/letsencrypt/:Z
+ - ./rln_tree:/etc/rln_tree/:Z
+ - ./keystore:/keystore:Z
+ entrypoint: sh
+ command:
+ - /usr/bin/run_tester_node.sh
+ - /usr/bin/liteprotocoltester
+ - SENDER
+ - waku-sim
+ depends_on:
+ - lightpush-service
+ configs:
+ - source: cfg_tester_node.toml
+ target: config.toml
+ networks:
+ - waku-simulator_simulation
+
+ filter-service:
+ image: ${NWAKU_IMAGE:-harbor.status.im/wakuorg/nwaku:latest-release}
+ # ports:
+ # - 30304:30305/tcp
+ # - 30304:30305/udp
+ # - 9005:9005/udp
+ # - 127.0.0.1:8003:8003
+ # - 80:80 #Let's Encrypt
+ # - 8000:8000/tcp #WSS
+ # - 127.0.0.1:8645:8645
+ <<:
+ - *logging
+ environment:
+ DOMAIN: ${DOMAIN}
+ RLN_RELAY_CRED_PASSWORD: "${RLN_RELAY_CRED_PASSWORD}"
+ ETH_CLIENT_ADDRESS: *eth_client_address
+ EXTRA_ARGS: ${EXTRA_ARGS}
+ <<:
+ - *rln_env
+ - *test_running_conditions
+ volumes:
+ - ./run_service_node.sh:/opt/run_service_node.sh:Z
+ - ${CERTS_DIR:-./certs}:/etc/letsencrypt/:Z
+ - ./rln_tree:/etc/rln_tree/:Z
+ - ./keystore:/keystore:Z
+ entrypoint: sh
+ command:
+ - /opt/run_service_node.sh
+ - FILTER
+ networks:
+ - waku-simulator_simulation
+
+
+ receivernode:
+ image: waku.liteprotocoltester:latest
+ build:
+ context: ../..
+ dockerfile: ./apps/liteprotocoltester/Dockerfile.liteprotocoltester
+ deploy:
+ replicas: ${NUM_RECEIVER_NODES:-1}
+ # ports:
+ # - 30304:30304/tcp
+ # - 30304:30304/udp
+ # - 9005:9005/udp
+ # - 127.0.0.1:8003:8003
+ # - 80:80 #Let's Encrypt
+ # - 8000:8000/tcp #WSS
+ # - 127.0.0.1:8647:8647
+ <<:
+ - *logging
+ environment:
+ DOMAIN: ${DOMAIN}
+ RLN_RELAY_CRED_PASSWORD: "${RLN_RELAY_CRED_PASSWORD}"
+ ETH_CLIENT_ADDRESS: *eth_client_address
+ EXTRA_ARGS: ${EXTRA_ARGS}
+ <<:
+ - *rln_env
+ - *test_running_conditions
+ volumes:
+ - ${CERTS_DIR:-./certs}:/etc/letsencrypt/:Z
+ - ./rln_tree:/etc/rln_tree/:Z
+ - ./keystore:/keystore:Z
+ entrypoint: sh
+ command:
+ - /usr/bin/run_tester_node.sh
+ - /usr/bin/liteprotocoltester
+ - RECEIVER
+ - waku-sim
+ depends_on:
+ - filter-service
+ - publishernode
+ configs:
+ - source: cfg_tester_node.toml
+ target: config.toml
+ networks:
+ - waku-simulator_simulation
+
+ # We have prometheus and grafana defined in waku-simulator already
+ prometheus:
+ image: docker.io/prom/prometheus:latest
+ volumes:
+ - ./monitoring/prometheus-config.yml:/etc/prometheus/prometheus.yml:Z
+ command:
+ - --config.file=/etc/prometheus/prometheus.yml
+ - --web.listen-address=:9099
+ # ports:
+ # - 127.0.0.1:9090:9090
+ restart: on-failure:5
+ depends_on:
+ - filter-service
+ - lightpush-service
+ - publishernode
+ - receivernode
+ networks:
+ - waku-simulator_simulation
+
+ grafana:
+ image: docker.io/grafana/grafana:latest
+ env_file:
+ - ./monitoring/configuration/grafana-plugins.env
+ volumes:
+ - ./monitoring/configuration/grafana.ini:/etc/grafana/grafana.ini:Z
+ - ./monitoring/configuration/dashboards.yaml:/etc/grafana/provisioning/dashboards/dashboards.yaml:Z
+ - ./monitoring/configuration/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml:Z
+ - ./monitoring/configuration/dashboards:/var/lib/grafana/dashboards/:Z
+ - ./monitoring/configuration/customizations/custom-logo.svg:/usr/share/grafana/public/img/grafana_icon.svg:Z
+ - ./monitoring/configuration/customizations/custom-logo.svg:/usr/share/grafana/public/img/grafana_typelogo.svg:Z
+ - ./monitoring/configuration/customizations/custom-logo.png:/usr/share/grafana/public/img/fav32.png:Z
+ ports:
+ - 0.0.0.0:3033:3033
+ restart: on-failure:5
+ depends_on:
+ - prometheus
+ networks:
+ - waku-simulator_simulation
+
+configs:
+ cfg_tester_node.toml:
+ content: |
+ max-connections = 100
+
+networks:
+ waku-simulator_simulation:
+ external: true
diff --git a/third-party/nwaku/apps/liteprotocoltester/docker-compose.yml b/third-party/nwaku/apps/liteprotocoltester/docker-compose.yml
new file mode 100644
index 0000000..16b5446
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/docker-compose.yml
@@ -0,0 +1,172 @@
+version: "3.7"
+x-logging: &logging
+ logging:
+ driver: json-file
+ options:
+ max-size: 1000m
+
+# Environment variable definitions
+x-eth-client-address: ð_client_address ${ETH_CLIENT_ADDRESS:-} # Add your ETH_CLIENT_ADDRESS after the "-"
+
+x-rln-environment: &rln_env
+ RLN_RELAY_CONTRACT_ADDRESS: ${RLN_RELAY_CONTRACT_ADDRESS:-0xB9cd878C90E49F797B4431fBF4fb333108CB90e6}
+ RLN_RELAY_CRED_PATH: ${RLN_RELAY_CRED_PATH:-} # Optional: Add your RLN_RELAY_CRED_PATH after the "-"
+ RLN_RELAY_CRED_PASSWORD: ${RLN_RELAY_CRED_PASSWORD:-} # Optional: Add your RLN_RELAY_CRED_PASSWORD after the "-"
+
+x-test-running-conditions: &test_running_conditions
+ NUM_MESSAGES: ${NUM_MESSAGES:-120}
+ MESSAGE_INTERVAL_MILLIS: "${MESSAGE_INTERVAL_MILLIS:-1000}"
+ SHARD: ${SHARD:-0}
+ CONTENT_TOPIC: ${CONTENT_TOPIC:-/tester/2/light-pubsub-test/wakusim}
+ CLUSTER_ID: ${CLUSTER_ID:-66}
+ MIN_MESSAGE_SIZE: ${MIN_MESSAGE_SIZE:-1Kb}
+ MAX_MESSAGE_SIZE: ${MAX_MESSAGE_SIZE:-150Kb}
+ START_PUBLISHING_AFTER_SECS: ${START_PUBLISHING_AFTER_SECS:-5} # seconds
+ STANDALONE: ${STANDALONE:-1}
+ RECEIVER_METRICS_PORT: 8003
+ PUBLISHER_METRICS_PORT: 8003
+
+
+# Services definitions
+services:
+ servicenode:
+ image: ${NWAKU_IMAGE:-harbor.status.im/wakuorg/nwaku:latest-release}
+ ports:
+ - 30304:30304/tcp
+ - 30304:30304/udp
+ - 9005:9005/udp
+ - 127.0.0.1:8003:8003
+ - 80:80 #Let's Encrypt
+ - 8000:8000/tcp #WSS
+ - 127.0.0.1:8645:8645
+ <<:
+ - *logging
+ environment:
+ DOMAIN: ${DOMAIN}
+ RLN_RELAY_CRED_PASSWORD: "${RLN_RELAY_CRED_PASSWORD}"
+ ETH_CLIENT_ADDRESS: *eth_client_address
+ EXTRA_ARGS: ${EXTRA_ARGS}
+ <<:
+ - *rln_env
+ - *test_running_conditions
+ volumes:
+ - ./run_service_node.sh:/opt/run_service_node.sh:Z
+ - ${CERTS_DIR:-./certs}:/etc/letsencrypt/:Z
+ - ./rln_tree:/etc/rln_tree/:Z
+ - ./keystore:/keystore:Z
+ entrypoint: sh
+ command:
+ - /opt/run_service_node.sh
+
+ publishernode:
+ image: waku.liteprotocoltester:latest
+ build:
+ context: ../..
+ dockerfile: ./apps/liteprotocoltester/Dockerfile.liteprotocoltester
+ ports:
+ # - 30304:30304/tcp
+ # - 30304:30304/udp
+ # - 9005:9005/udp
+ # - 127.0.0.1:8003:8003
+ # - 80:80 #Let's Encrypt
+ # - 8000:8000/tcp #WSS
+ - 127.0.0.1:8646:8646
+ <<:
+ - *logging
+ environment:
+ DOMAIN: ${DOMAIN}
+ RLN_RELAY_CRED_PASSWORD: "${RLN_RELAY_CRED_PASSWORD}"
+ ETH_CLIENT_ADDRESS: *eth_client_address
+ EXTRA_ARGS: ${EXTRA_ARGS}
+ <<:
+ - *rln_env
+ - *test_running_conditions
+ volumes:
+ - ${CERTS_DIR:-./certs}:/etc/letsencrypt/:Z
+ - ./rln_tree:/etc/rln_tree/:Z
+ - ./keystore:/keystore:Z
+ entrypoint: sh
+ command:
+ - /usr/bin/run_tester_node.sh
+ - /usr/bin/liteprotocoltester
+ - SENDER
+ - servicenode
+ depends_on:
+ - servicenode
+ configs:
+ - source: cfg_tester_node.toml
+ target: config.toml
+
+ receivernode:
+ image: waku.liteprotocoltester:latest
+ build:
+ context: ../..
+ dockerfile: ./apps/liteprotocoltester/Dockerfile.liteprotocoltester
+ ports:
+ # - 30304:30304/tcp
+ # - 30304:30304/udp
+ # - 9005:9005/udp
+ # - 127.0.0.1:8003:8003
+ # - 80:80 #Let's Encrypt
+ # - 8000:8000/tcp #WSS
+ - 127.0.0.1:8647:8647
+ <<:
+ - *logging
+ environment:
+ DOMAIN: ${DOMAIN}
+ RLN_RELAY_CRED_PASSWORD: "${RLN_RELAY_CRED_PASSWORD}"
+ ETH_CLIENT_ADDRESS: *eth_client_address
+ EXTRA_ARGS: ${EXTRA_ARGS}
+ <<:
+ - *rln_env
+ - *test_running_conditions
+ volumes:
+ - ./run_tester_node.sh:/opt/run_tester_node.sh:Z
+ - ${CERTS_DIR:-./certs}:/etc/letsencrypt/:Z
+ - ./rln_tree:/etc/rln_tree/:Z
+ - ./keystore:/keystore:Z
+ entrypoint: sh
+ command:
+ - /usr/bin/run_tester_node.sh
+ - /usr/bin/liteprotocoltester
+ - RECEIVER
+ - servicenode
+ depends_on:
+ - servicenode
+ - publishernode
+ configs:
+ - source: cfg_tester_node.toml
+ target: config.toml
+
+ prometheus:
+ image: docker.io/prom/prometheus:latest
+ volumes:
+ - ./monitoring/prometheus-config.yml:/etc/prometheus/prometheus.yml:Z
+ command:
+ - --config.file=/etc/prometheus/prometheus.yml
+ ports:
+ - 127.0.0.1:9090:9090
+ depends_on:
+ - servicenode
+
+ grafana:
+ image: docker.io/grafana/grafana:latest
+ env_file:
+ - ./monitoring/configuration/grafana-plugins.env
+ volumes:
+ - ./monitoring/configuration/grafana.ini:/etc/grafana/grafana.ini:Z
+ - ./monitoring/configuration/dashboards.yaml:/etc/grafana/provisioning/dashboards/dashboards.yaml:Z
+ - ./monitoring/configuration/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml:Z
+ - ./monitoring/configuration/dashboards:/var/lib/grafana/dashboards/:Z
+ - ./monitoring/configuration/customizations/custom-logo.svg:/usr/share/grafana/public/img/grafana_icon.svg:Z
+ - ./monitoring/configuration/customizations/custom-logo.svg:/usr/share/grafana/public/img/grafana_typelogo.svg:Z
+ - ./monitoring/configuration/customizations/custom-logo.png:/usr/share/grafana/public/img/fav32.png:Z
+ ports:
+ - 0.0.0.0:3000:3000
+ depends_on:
+ - prometheus
+
+configs:
+ cfg_tester_node.toml:
+ content: |
+ max-connections = 100
diff --git a/third-party/nwaku/apps/liteprotocoltester/infra.env b/third-party/nwaku/apps/liteprotocoltester/infra.env
new file mode 100644
index 0000000..ebf6147
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/infra.env
@@ -0,0 +1,11 @@
+TEST_INTERVAL_MINUTES=180
+START_PUBLISHING_AFTER_SECS=120
+NUM_MESSAGES=300
+MESSAGE_INTERVAL_MILLIS=1000
+MIN_MESSAGE_SIZE=15Kb
+MAX_MESSAGE_SIZE=145Kb
+SHARD=32
+CONTENT_TOPIC=/tester/2/light-pubsub-test-at-infra/status-prod
+CLUSTER_ID=16
+LIGHTPUSH_BOOTSTRAP=enr:-QEKuED9AJm2HGgrRpVaJY2nj68ao_QiPeUT43sK-aRM7sMJ6R4G11OSDOwnvVacgN1sTw-K7soC5dzHDFZgZkHU0u-XAYJpZIJ2NIJpcISnYxMvim11bHRpYWRkcnO4WgAqNiVib290LTAxLmRvLWFtczMuc3RhdHVzLnByb2Quc3RhdHVzLmltBnZfACw2JWJvb3QtMDEuZG8tYW1zMy5zdGF0dXMucHJvZC5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEC3rRtFQSgc24uWewzXaxTY8hDAHB8sgnxr9k8Rjb5GeSDdGNwgnZfg3VkcIIjKIV3YWt1Mg0
+FILTER_BOOTSTRAP=enr:-QEcuED7ww5vo2rKc1pyBp7fubBUH-8STHEZHo7InjVjLblEVyDGkjdTI9VdqmYQOn95vuQH-Htku17WSTzEufx-Wg4mAYJpZIJ2NIJpcIQihw1Xim11bHRpYWRkcnO4bAAzNi5ib290LTAxLmdjLXVzLWNlbnRyYWwxLWEuc3RhdHVzLnByb2Quc3RhdHVzLmltBnZfADU2LmJvb3QtMDEuZ2MtdXMtY2VudHJhbDEtYS5zdGF0dXMucHJvZC5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaECxjqgDQ0WyRSOilYU32DA5k_XNlDis3m1VdXkK9xM6kODdGNwgnZfg3VkcIIjKIV3YWt1Mg0
diff --git a/third-party/nwaku/apps/liteprotocoltester/legacy_publisher.nim b/third-party/nwaku/apps/liteprotocoltester/legacy_publisher.nim
new file mode 100644
index 0000000..12733ad
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/legacy_publisher.nim
@@ -0,0 +1,24 @@
+import chronos, results, options
+import waku/[waku_node, waku_core]
+import publisher_base
+
+type LegacyPublisher* = ref object of PublisherBase
+
+proc new*(T: type LegacyPublisher, wakuNode: WakuNode): T =
+ if isNil(wakuNode.wakuLegacyLightpushClient):
+ wakuNode.mountLegacyLightPushClient()
+
+ return LegacyPublisher(wakuNode: wakuNode)
+
+method send*(
+ self: LegacyPublisher,
+ topic: PubsubTopic,
+ message: WakuMessage,
+ servicePeer: RemotePeerInfo,
+): Future[Result[void, string]] {.async.} =
+ # when error it must return original error desc due the text is used for distinction between error types in metrics.
+ discard (
+ await self.wakuNode.legacyLightpushPublish(some(topic), message, servicePeer)
+ ).valueOr:
+ return err(error)
+ return ok()
diff --git a/third-party/nwaku/apps/liteprotocoltester/liteprotocoltester.nim b/third-party/nwaku/apps/liteprotocoltester/liteprotocoltester.nim
new file mode 100644
index 0000000..2db9bf5
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/liteprotocoltester.nim
@@ -0,0 +1,217 @@
+{.push raises: [].}
+
+import
+ std/[options, strutils, os, sequtils, net],
+ chronicles,
+ chronos,
+ metrics,
+ libbacktrace,
+ system/ansi_c,
+ libp2p/crypto/crypto,
+ confutils
+
+import
+ ../../tools/confutils/cli_args,
+ waku/[
+ common/enr,
+ common/logging,
+ factory/waku as waku_factory,
+ waku_node,
+ node/waku_metrics,
+ node/peer_manager,
+ waku_lightpush/common,
+ waku_filter_v2,
+ waku_peer_exchange/protocol,
+ waku_core/peers,
+ waku_core/multiaddrstr,
+ ],
+ ./tester_config,
+ ./publisher,
+ ./receiver,
+ ./diagnose_connections,
+ ./service_peer_management
+
+logScope:
+ topics = "liteprotocoltester main"
+
+proc logConfig(conf: LiteProtocolTesterConf) =
+ info "Configuration: Lite protocol tester", conf = $conf
+
+{.pop.}
+when isMainModule:
+ ## Node setup happens in 6 phases:
+ ## 1. Set up storage
+ ## 2. Initialize node
+ ## 3. Mount and initialize configured protocols
+ ## 4. Start node and mounted protocols
+ ## 5. Start monitoring tools and external interfaces
+ ## 6. Setup graceful shutdown hooks
+
+ const versionString = "version / git commit hash: " & waku_factory.git_version
+
+ let confRes = LiteProtocolTesterConf.load(version = versionString)
+ if confRes.isErr():
+ error "failure while loading the configuration", error = confRes.error
+ quit(QuitFailure)
+
+ var conf = confRes.get()
+
+ ## Logging setup
+ logging.setupLog(conf.logLevel, conf.logFormat)
+
+ info "Running Lite Protocol Tester node", version = waku_factory.git_version
+ logConfig(conf)
+
+ ##Prepare Waku configuration
+ ## - load from config file
+ ## - override according to tester functionality
+ ##
+
+ var wakuNodeConf: WakuNodeConf
+
+ if conf.configFile.isSome():
+ try:
+ var configFile {.threadvar.}: InputFile
+ configFile = conf.configFile.get()
+ wakuNodeConf = WakuNodeConf.load(
+ version = versionString,
+ printUsage = false,
+ secondarySources = proc(
+ wnconf: WakuNodeConf, sources: auto
+ ) {.gcsafe, raises: [ConfigurationError].} =
+ echo "Loading secondary configuration file into WakuNodeConf"
+ sources.addConfigFile(Toml, configFile),
+ )
+ except CatchableError:
+ error "Loading Waku configuration failed", error = getCurrentExceptionMsg()
+ quit(QuitFailure)
+
+ wakuNodeConf.logLevel = conf.logLevel
+ wakuNodeConf.logFormat = conf.logFormat
+ wakuNodeConf.nat = conf.nat
+ wakuNodeConf.maxConnections = 500
+ wakuNodeConf.restAddress = conf.restAddress
+ wakuNodeConf.restPort = conf.restPort
+ wakuNodeConf.restAllowOrigin = conf.restAllowOrigin
+
+ wakuNodeConf.dnsAddrsNameServers =
+ @[parseIpAddress("8.8.8.8"), parseIpAddress("1.1.1.1")]
+
+ wakuNodeConf.shards = @[conf.shard]
+ wakuNodeConf.contentTopics = conf.contentTopics
+ wakuNodeConf.clusterId = conf.clusterId
+ ## TODO: Depending on the tester needs we might extend here with shards, clusterId, etc...
+
+ wakuNodeConf.metricsServer = true
+ wakuNodeConf.metricsServerAddress = parseIpAddress("0.0.0.0")
+ wakuNodeConf.metricsServerPort = conf.metricsPort
+
+ # If bootstrap option is chosen we expect our clients will not mounted
+ # so we will mount PeerExchange manually to gather possible service peers,
+ # if got some we will mount the client protocols afterward.
+ wakuNodeConf.peerExchange = false
+ wakuNodeConf.relay = false
+ wakuNodeConf.filter = false
+ wakuNodeConf.lightpush = false
+ wakuNodeConf.store = false
+
+ wakuNodeConf.rest = false
+ wakuNodeConf.relayServiceRatio = "40:60"
+
+ let wakuConf = wakuNodeConf.toWakuConf().valueOr:
+ error "Issue converting toWakuConf", error = $error
+ quit(QuitFailure)
+
+ var waku = (waitFor Waku.new(wakuConf)).valueOr:
+ error "Waku initialization failed", error = error
+ quit(QuitFailure)
+
+ (waitFor startWaku(addr waku)).isOkOr:
+ error "Starting waku failed", error = error
+ quit(QuitFailure)
+
+ debug "Setting up shutdown hooks"
+
+ proc asyncStopper(waku: Waku) {.async: (raises: [Exception]).} =
+ await waku.stop()
+ quit(QuitSuccess)
+
+ # Handle Ctrl-C SIGINT
+ proc handleCtrlC() {.noconv.} =
+ when defined(windows):
+ # workaround for https://github.com/nim-lang/Nim/issues/4057
+ setupForeignThreadGc()
+ notice "Shutting down after receiving SIGINT"
+ asyncSpawn asyncStopper(waku)
+
+ setControlCHook(handleCtrlC)
+
+ # Handle SIGTERM
+ when defined(posix):
+ proc handleSigterm(signal: cint) {.noconv.} =
+ notice "Shutting down after receiving SIGTERM"
+ asyncSpawn asyncStopper(waku)
+
+ c_signal(ansi_c.SIGTERM, handleSigterm)
+
+ # Handle SIGSEGV
+ when defined(posix):
+ proc handleSigsegv(signal: cint) {.noconv.} =
+ # Require --debugger:native
+ fatal "Shutting down after receiving SIGSEGV", stacktrace = getBacktrace()
+
+ # Not available in -d:release mode
+ writeStackTrace()
+
+ waitFor waku.stop()
+ quit(QuitFailure)
+
+ c_signal(ansi_c.SIGSEGV, handleSigsegv)
+
+ info "Node setup complete"
+
+ var codec = WakuLightPushCodec
+ # mounting relevant client, for PX filter client must be mounted ahead
+ if conf.testFunc == TesterFunctionality.SENDER:
+ codec = WakuLightPushCodec
+ else:
+ codec = WakuFilterSubscribeCodec
+
+ var lookForServiceNode = false
+ var serviceNodePeerInfo: RemotePeerInfo
+ if conf.serviceNode.len == 0:
+ if conf.bootstrapNode.len > 0:
+ info "Bootstrapping with PeerExchange to gather random service node"
+ let futForServiceNode = pxLookupServiceNode(waku.node, conf)
+ if not (waitFor futForServiceNode.withTimeout(20.minutes)):
+ error "Service node not found in time via PX"
+ quit(QuitFailure)
+
+ if futForServiceNode.read().isErr():
+ error "Service node for test not found via PX"
+ quit(QuitFailure)
+
+ serviceNodePeerInfo = selectRandomServicePeer(
+ waku.node.peerManager, none(RemotePeerInfo), codec
+ ).valueOr:
+ error "Service node selection failed"
+ quit(QuitFailure)
+ else:
+ error "No service or bootstrap node provided"
+ quit(QuitFailure)
+ else:
+ # support for both ENR and URI formatted service node addresses
+ serviceNodePeerInfo = translateToRemotePeerInfo(conf.serviceNode).valueOr:
+ error "failed to parse service-node", node = conf.serviceNode
+ quit(QuitFailure)
+
+ info "Service node to be used", serviceNode = $serviceNodePeerInfo
+
+ logSelfPeers(waku.node.peerManager)
+
+ if conf.testFunc == TesterFunctionality.SENDER:
+ setupAndPublish(waku.node, conf, serviceNodePeerInfo)
+ else:
+ setupAndListen(waku.node, conf, serviceNodePeerInfo)
+
+ runForever()
diff --git a/third-party/nwaku/apps/liteprotocoltester/lpt_metrics.nim b/third-party/nwaku/apps/liteprotocoltester/lpt_metrics.nim
new file mode 100644
index 0000000..8b30619
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/lpt_metrics.nim
@@ -0,0 +1,56 @@
+## Example showing how a resource restricted client may
+## subscribe to messages without relay
+
+import metrics
+
+export metrics
+
+declarePublicGauge lpt_receiver_sender_peer_count, "count of sender peers"
+
+declarePublicCounter lpt_receiver_received_messages_count,
+ "number of messages received per peer", ["peer"]
+
+declarePublicCounter lpt_receiver_received_bytes,
+ "number of received bytes per peer", ["peer"]
+
+declarePublicGauge lpt_receiver_missing_messages_count,
+ "number of missing messages per peer", ["peer"]
+
+declarePublicCounter lpt_receiver_duplicate_messages_count,
+ "number of duplicate messages per peer", ["peer"]
+
+declarePublicGauge lpt_receiver_distinct_duplicate_messages_count,
+ "number of distinct duplicate messages per peer", ["peer"]
+
+declarePublicGauge lpt_receiver_latencies,
+ "Message delivery latency per peer (min-avg-max)", ["peer", "latency"]
+
+declarePublicCounter lpt_receiver_lost_subscription_count,
+ "number of filter service peer failed PING requests - lost subscription"
+
+declarePublicCounter lpt_publisher_sent_messages_count, "number of messages published"
+
+declarePublicCounter lpt_publisher_failed_messages_count,
+ "number of messages failed to publish per failure cause", ["cause"]
+
+declarePublicCounter lpt_publisher_sent_bytes, "number of total bytes sent"
+
+declarePublicCounter lpt_service_peer_failure_count,
+ "number of failure during using service peer [publisher/receiever]", ["role", "agent"]
+
+declarePublicCounter lpt_change_service_peer_count,
+ "number of times [publisher/receiver] had to change service peer", ["role"]
+
+declarePublicGauge lpt_px_peers,
+ "Number of peers PeerExchange discovered and can be dialed"
+
+declarePublicGauge lpt_dialed_peers, "Number of peers successfully dialed", ["agent"]
+
+declarePublicGauge lpt_dial_failures, "Number of dial failures by cause", ["agent"]
+
+declarePublicHistogram lpt_publish_duration_seconds,
+ "duration to lightpush messages",
+ buckets = [
+ 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0,
+ 15.0, 20.0, 30.0, Inf,
+ ]
diff --git a/third-party/nwaku/apps/liteprotocoltester/lpt_supervisor.py b/third-party/nwaku/apps/liteprotocoltester/lpt_supervisor.py
new file mode 100755
index 0000000..7d882af
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/lpt_supervisor.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+
+import os
+import time
+from subprocess import Popen
+import sys
+
+def load_env(file_path):
+ predefined_test_env = {}
+ with open(file_path) as f:
+ for line in f:
+ if line.strip() and not line.startswith('#'):
+ key, value = line.strip().split('=', 1)
+ predefined_test_env[key] = value
+ return predefined_test_env
+
+def run_tester_node(predefined_test_env):
+ role = sys.argv[1]
+ # override incoming environment variables with the ones from the file to prefer predefined testing environment.
+ for key, value in predefined_test_env.items():
+ os.environ[key] = value
+
+ script_cmd = "/usr/bin/run_tester_node_at_infra.sh /usr/bin/liteprotocoltester {role}".format(role=role)
+ return os.system(script_cmd)
+
+if __name__ == "__main__":
+ if len(sys.argv) < 2 or sys.argv[1] not in ["RECEIVER", "SENDER", "SENDERV3"]:
+ print("Error: First argument must be either 'RECEIVER' or 'SENDER' or 'SENDERV3'")
+ sys.exit(1)
+
+ predefined_test_env_file = '/usr/bin/infra.env'
+ predefined_test_env = load_env(predefined_test_env_file)
+
+ test_interval_minutes = int(predefined_test_env.get('TEST_INTERVAL_MINUTES', 60)) # Default to 60 minutes if not set
+ print(f"supervisor: Start testing loop. Interval is {test_interval_minutes} minutes")
+ counter = 0
+
+ while True:
+ counter += 1
+ start_time = time.time()
+ print(f"supervisor: Run #{counter} started at {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start_time))}")
+ print(f"supervisor: with arguments: {predefined_test_env}")
+
+ exit_code = run_tester_node(predefined_test_env)
+
+ end_time = time.time()
+ run_time = end_time - start_time
+ sleep_time = max(5 * 60, (test_interval_minutes * 60) - run_time)
+
+ print(f"supervisor: Tester node finished at {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(end_time))}")
+ print(f"supervisor: Runtime was {run_time:.2f} seconds")
+ print(f"supervisor: Next run scheduled in {sleep_time // 60:.2f} minutes")
+
+ time.sleep(sleep_time)
diff --git a/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/customizations/custom-logo.png b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/customizations/custom-logo.png
new file mode 100644
index 0000000..dcf13b9
Binary files /dev/null and b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/customizations/custom-logo.png differ
diff --git a/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/customizations/custom-logo.svg b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/customizations/custom-logo.svg
new file mode 100644
index 0000000..3c9a6da
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/customizations/custom-logo.svg
@@ -0,0 +1,3 @@
+
diff --git a/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/dashboards.yaml b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/dashboards.yaml
new file mode 100644
index 0000000..e59ac96
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/dashboards.yaml
@@ -0,0 +1,9 @@
+apiVersion: 1
+
+providers:
+- name: 'Prometheus'
+ orgId: 1
+ folder: ''
+ type: file
+ options:
+ path: /var/lib/grafana/dashboards
\ No newline at end of file
diff --git a/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/dashboards/liter-protocol-test-monitoring.json b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/dashboards/liter-protocol-test-monitoring.json
new file mode 100644
index 0000000..22770e2
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/dashboards/liter-protocol-test-monitoring.json
@@ -0,0 +1,1949 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "grafana",
+ "uid": "-- Grafana --"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "description": "Monitoring of lite-protocol-tester's send/receiver performance and failure counters.",
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "links": [],
+ "panels": [
+ {
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 13,
+ "panels": [],
+ "title": "Peer statistics",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "continuous-GrYlRd"
+ },
+ "fieldMinMax": false,
+ "mappings": [],
+ "max": 100,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 15,
+ "w": 5,
+ "x": 0,
+ "y": 1
+ },
+ "id": 15,
+ "options": {
+ "displayMode": "lcd",
+ "maxVizHeight": 300,
+ "minVizHeight": 16,
+ "minVizWidth": 8,
+ "namePlacement": "auto",
+ "orientation": "horizontal",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showUnfilled": true,
+ "sizing": "auto",
+ "valueMode": "color"
+ },
+ "pluginVersion": "11.2.0",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "lpt_px_peers{instance=~\".*publisher.*\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Lightpush capable peers found via PX",
+ "type": "bargauge"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 15,
+ "w": 7,
+ "x": 5,
+ "y": 1
+ },
+ "id": 22,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "11.2.0",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "lpt_dialed_peers{instance=~\".*publisher.*\"}",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Working filter peers {{instance}}",
+ "range": true,
+ "refId": "B",
+ "useBackend": false
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "lpt_dial_failures{instance=~\".*publisher.*\"}",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Failed to dial {{instance}}",
+ "range": true,
+ "refId": "C",
+ "useBackend": false
+ }
+ ],
+ "title": "Tested lightpush peers",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "continuous-GrYlRd"
+ },
+ "fieldMinMax": false,
+ "mappings": [],
+ "max": 100,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 15,
+ "w": 5,
+ "x": 12,
+ "y": 1
+ },
+ "id": 21,
+ "options": {
+ "displayMode": "lcd",
+ "maxVizHeight": 300,
+ "minVizHeight": 16,
+ "minVizWidth": 8,
+ "namePlacement": "auto",
+ "orientation": "horizontal",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showUnfilled": true,
+ "sizing": "auto",
+ "valueMode": "color"
+ },
+ "pluginVersion": "11.2.0",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "lpt_px_peers{instance=~\".*receiver.*\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Filter capable peers found via PX",
+ "type": "bargauge"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 15,
+ "w": 7,
+ "x": 17,
+ "y": 1
+ },
+ "id": 14,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "11.2.0",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "lpt_dialed_peers{instance=~\".*receivernode.*\"}",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Working filter peers {{instance}}",
+ "range": true,
+ "refId": "B",
+ "useBackend": false
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "lpt_dial_failures{instance=~\".*receivernode.*\"}",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Failed to dial {{instance}}",
+ "range": true,
+ "refId": "C",
+ "useBackend": false
+ }
+ ],
+ "title": "Tested filter peers",
+ "type": "timeseries"
+ },
+ {
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 16
+ },
+ "id": 12,
+ "title": "Test publisher monitor",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 23,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 17
+ },
+ "id": 16,
+ "options": {
+ "legend": {
+ "calcs": [
+ "last"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum by(instace) (lpt_service_peer_failure_count_total{instance=~\".*publishernode.*\", role=\"publisher\"})",
+ "format": "time_series",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Push failed",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "sum by(instace) (lpt_change_service_peer_count_total{instance=~\".*publishernode.*\", role=\"publisher\"})",
+ "hide": false,
+ "instant": false,
+ "legendFormat": "Peer switch",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Lightpush service peer failures and switches",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 23,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 17
+ },
+ "id": 17,
+ "options": {
+ "legend": {
+ "calcs": [
+ "last"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum by(instace) (lpt_service_peer_failure_count_total{instance=~\".*receivernode.*\", role=\"receiver\"})",
+ "format": "time_series",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Subscribe failed",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "sum by(instace) (lpt_change_service_peer_count_total{instance=~\".*receivernode.*\", role=\"receiver\"})",
+ "hide": false,
+ "instant": false,
+ "legendFormat": "Peer switch",
+ "range": true,
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "sum by(instace) (lpt_receiver_lost_subscription_count_total{instance=~\".*receivernode.*\"})",
+ "hide": false,
+ "instant": false,
+ "legendFormat": "Subscription loss - ping fail",
+ "range": true,
+ "refId": "C"
+ }
+ ],
+ "title": "Filter service peer failures and switches",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "mappings": [],
+ "thresholds": {
+ "mode": "percentage",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "orange",
+ "value": 70
+ },
+ {
+ "color": "red",
+ "value": 85
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 12,
+ "w": 12,
+ "x": 0,
+ "y": 25
+ },
+ "id": 18,
+ "options": {
+ "minVizHeight": 75,
+ "minVizWidth": 75,
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showThresholdLabels": false,
+ "showThresholdMarkers": false,
+ "sizing": "auto"
+ },
+ "pluginVersion": "11.2.0",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "count(\n group(\n last_over_time(lpt_px_peers{instance=~\".*publishernode.*\"}[24h])\n ) by (instance)\n)",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Number or publishers",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "count(\n group(\n last_over_time(lpt_px_peers{instance=~\".*receivernode.*\"}[24h])\n ) by (instance)\n)",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Number or receivers",
+ "range": true,
+ "refId": "B",
+ "useBackend": false
+ }
+ ],
+ "title": "Number of tester nodes",
+ "type": "gauge"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 12,
+ "w": 12,
+ "x": 12,
+ "y": 25
+ },
+ "id": 8,
+ "options": {
+ "displayMode": "lcd",
+ "maxVizHeight": 300,
+ "minVizHeight": 16,
+ "minVizWidth": 8,
+ "namePlacement": "top",
+ "orientation": "horizontal",
+ "reduceOptions": {
+ "calcs": [
+ "last"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showUnfilled": true,
+ "sizing": "auto",
+ "valueMode": "color"
+ },
+ "pluginVersion": "11.2.0",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "lpt_receiver_sender_peer_count{instance=~\".*receivernode.*\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Receiver detected message from number of publisher peers",
+ "type": "bargauge"
+ },
+ {
+ "collapsed": true,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 37
+ },
+ "id": 11,
+ "panels": [],
+ "title": "Test performance",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Publishing rate"
+ },
+ "properties": [
+ {
+ "id": "custom.axisPlacement",
+ "value": "right"
+ },
+ {
+ "id": "custom.gradientMode",
+ "value": "hue"
+ },
+ {
+ "id": "custom.fillOpacity",
+ "value": 15
+ },
+ {
+ "id": "custom.stacking",
+ "value": {
+ "group": "A",
+ "mode": "normal"
+ }
+ },
+ {
+ "id": "unit",
+ "value": "reqps"
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 38
+ },
+ "id": 1,
+ "options": {
+ "legend": {
+ "calcs": [
+ "last"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum by(instace) (lpt_publisher_sent_messages_count_total{instance=~\".*publishernode.*\"})",
+ "format": "time_series",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Total published count",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum by(instance) (rate(lpt_publisher_sent_messages_count_total{instance=~\".*publishernode.*\"}[$__rate_interval]))",
+ "format": "time_series",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Publishing rate",
+ "range": true,
+ "refId": "B",
+ "useBackend": false
+ }
+ ],
+ "title": "Published test messages",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "series",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Received message rate"
+ },
+ "properties": [
+ {
+ "id": "custom.axisPlacement",
+ "value": "right"
+ },
+ {
+ "id": "custom.scaleDistribution",
+ "value": {
+ "type": "linear"
+ }
+ },
+ {
+ "id": "custom.fillOpacity",
+ "value": 9
+ },
+ {
+ "id": "custom.gradientMode",
+ "value": "hue"
+ },
+ {
+ "id": "unit",
+ "value": "reqps"
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 38
+ },
+ "id": 2,
+ "options": {
+ "legend": {
+ "calcs": [
+ "max"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "sum by(instance) (lpt_receiver_received_messages_count_total{instance=~\".*receivernode.*\"})",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Total message received",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "sum by(instance) (rate(lpt_receiver_received_messages_count_total{instance=~\".*receivernode.*\"}[$__rate_interval]))",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Received message rate",
+ "range": true,
+ "refId": "B",
+ "useBackend": false
+ }
+ ],
+ "title": "Received test messages",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "series",
+ "axisLabel": "",
+ "axisPlacement": "left",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "kbytes"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Send message transfer rate"
+ },
+ "properties": [
+ {
+ "id": "custom.axisPlacement",
+ "value": "right"
+ },
+ {
+ "id": "custom.drawStyle",
+ "value": "line"
+ },
+ {
+ "id": "custom.fillOpacity",
+ "value": 22
+ },
+ {
+ "id": "custom.scaleDistribution",
+ "value": {
+ "type": "linear"
+ }
+ },
+ {
+ "id": "custom.stacking",
+ "value": {
+ "group": "A",
+ "mode": "normal"
+ }
+ },
+ {
+ "id": "unit",
+ "value": "KiBs"
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 46
+ },
+ "id": 5,
+ "options": {
+ "legend": {
+ "calcs": [
+ "last"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum by(instance) (lpt_publisher_sent_bytes_total{instance=~\".*publishernode.*\"})",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Total sent bytes",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum by(instance) (rate(lpt_publisher_sent_bytes_total{instance=~\".*publishernode.*\"}[$__rate_interval]))",
+ "format": "time_series",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Send message transfer rate",
+ "range": true,
+ "refId": "B",
+ "useBackend": false
+ }
+ ],
+ "title": "Sent bytes",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "series",
+ "axisLabel": "",
+ "axisPlacement": "left",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "kbytes"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Test message transfer rate"
+ },
+ "properties": [
+ {
+ "id": "custom.axisPlacement",
+ "value": "right"
+ },
+ {
+ "id": "custom.drawStyle",
+ "value": "line"
+ },
+ {
+ "id": "custom.fillOpacity",
+ "value": 22
+ },
+ {
+ "id": "custom.scaleDistribution",
+ "value": {
+ "type": "linear"
+ }
+ },
+ {
+ "id": "custom.stacking",
+ "value": {
+ "group": "A",
+ "mode": "normal"
+ }
+ },
+ {
+ "id": "unit",
+ "value": "KiBs"
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 46
+ },
+ "id": 4,
+ "options": {
+ "legend": {
+ "calcs": [
+ "last"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum by(instance) (lpt_receiver_received_bytes_total{instance=~\".*receivernode.*\"})",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Total received bytes",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum by(instance) (rate(lpt_receiver_received_bytes_total{instance=~\".*receivernode.*\"}[$__rate_interval]))",
+ "format": "time_series",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Test message transfer rate",
+ "range": true,
+ "refId": "B",
+ "useBackend": false
+ }
+ ],
+ "title": "Received bytes",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 54
+ },
+ "id": 10,
+ "panels": [],
+ "title": "Failure statistics",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 23,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Published message rate"
+ },
+ "properties": [
+ {
+ "id": "custom.axisPlacement",
+ "value": "right"
+ },
+ {
+ "id": "custom.gradientMode",
+ "value": "hue"
+ },
+ {
+ "id": "custom.fillOpacity",
+ "value": 15
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 55
+ },
+ "id": 6,
+ "options": {
+ "legend": {
+ "calcs": [
+ "last"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "lpt_publisher_failed_messages_count_total{instance=~\".*publishernode.*\"}",
+ "format": "time_series",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{instance}} - {{cause}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Failed publish count per cause",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "series",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 55
+ },
+ "id": 7,
+ "options": {
+ "legend": {
+ "calcs": [
+ "last"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "lpt_receiver_duplicate_messages_count_total{instance=~\".*receivernode.*\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Total duplicates at {{instance}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "lpt_receiver_distinct_duplicate_messages_count{instance=~\".*receivernode.*\"}",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Distinct duplicates at {{instance}}",
+ "range": true,
+ "refId": "B",
+ "useBackend": false
+ }
+ ],
+ "title": "Received duplicated messages",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "series",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 21,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 63
+ },
+ "id": 9,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "lpt_receiver_missing_messages_count{instance=~\".*receivernode.*\"}",
+ "format": "time_series",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Receiver {{instance}}:Publisher {{peer}}",
+ "range": true,
+ "refId": "B",
+ "useBackend": false
+ }
+ ],
+ "title": "Not arrived messages",
+ "type": "timeseries"
+ }
+ ],
+ "refresh": "",
+ "schemaVersion": 39,
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "current": {
+ "selected": true,
+ "text": "lpt-runner-publishernode-1:8003",
+ "value": "lpt-runner-publishernode-1:8003"
+ },
+ "definition": "label_values({instance=~\".*publishernode.*\"},instance)",
+ "hide": 0,
+ "includeAll": false,
+ "multi": false,
+ "name": "publisher",
+ "options": [],
+ "query": {
+ "qryType": 1,
+ "query": "label_values({instance=~\".*publishernode.*\"},instance)",
+ "refId": "PrometheusVariableQueryEditor-VariableQuery"
+ },
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "type": "query"
+ },
+ {
+ "current": {
+ "selected": true,
+ "text": "lpt-runner-receivernode-1:8003",
+ "value": "lpt-runner-receivernode-1:8003"
+ },
+ "definition": "label_values({instance=~\".*receivernode.*\"},instance)",
+ "hide": 0,
+ "includeAll": false,
+ "multi": false,
+ "name": "receiver",
+ "options": [],
+ "query": {
+ "qryType": 1,
+ "query": "label_values({instance=~\".*receivernode.*\"},instance)",
+ "refId": "PrometheusVariableQueryEditor-VariableQuery"
+ },
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "type": "query"
+ }
+ ]
+ },
+ "time": {
+ "from": "2024-10-02T22:07:37.000Z",
+ "to": "2024-10-02T22:23:21.000Z"
+ },
+ "timepicker": {},
+ "timezone": "browser",
+ "title": "Liteprotocoltester monitoring",
+ "uid": "fdw6pgh9odszkd",
+ "version": 1,
+ "weekStart": ""
+}
diff --git a/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/datasources.yaml b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/datasources.yaml
new file mode 100644
index 0000000..2cc211f
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/datasources.yaml
@@ -0,0 +1,11 @@
+apiVersion: 1
+
+datasources:
+ - name: Prometheus
+ type: prometheus
+ access: proxy
+ org_id: 1
+ url: http://prometheus:9099
+ is_default: true
+ version: 1
+ editable: true
diff --git a/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/grafana-plugins.env b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/grafana-plugins.env
new file mode 100644
index 0000000..2780809
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/grafana-plugins.env
@@ -0,0 +1,2 @@
+#GF_INSTALL_PLUGINS=grafana-worldmap-panel,grafana-piechart-panel,digrich-bubblechart-panel,yesoreyeram-boomtheme-panel,briangann-gauge-panel,jdbranham-diagram-panel,agenty-flowcharting-panel,citilogics-geoloop-panel,savantly-heatmap-panel,mtanda-histogram-panel,pierosavi-imageit-panel,michaeldmoore-multistat-panel,zuburqan-parity-report-panel,natel-plotly-panel,bessler-pictureit-panel,grafana-polystat-panel,corpglory-progresslist-panel,snuids-radar-panel,fzakaria-simple-config.config.annotations-datasource,vonage-status-panel,snuids-trafficlights-panel,pr0ps-trackmap-panel,alexandra-trackmap-panel,btplc-trend-box-panel
+GF_INSTALL_PLUGINS=grafana-worldmap-panel,grafana-piechart-panel,yesoreyeram-boomtheme-panel,briangann-gauge-panel,pierosavi-imageit-panel,bessler-pictureit-panel,vonage-status-panel
diff --git a/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/grafana.ini b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/grafana.ini
new file mode 100644
index 0000000..631fbb7
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/monitoring/configuration/grafana.ini
@@ -0,0 +1,53 @@
+instance_name = liteprotocoltester dashboard
+
+;[dashboards.json]
+;enabled = true
+;path = /home/git/grafana/grafana-dashboards/dashboards
+
+[server]
+http_port = 3033
+
+#################################### Auth ##########################
+[auth]
+disable_login_form = false
+
+#################################### Anonymous Auth ##########################
+[auth.anonymous]
+# enable anonymous access
+enabled = true
+
+# specify organization name that should be used for unauthenticated users
+;org_name = Public
+
+# specify role for unauthenticated users
+org_role = Admin
+; org_role = Viewer
+
+;[security]
+;admin_user = ocr
+;admin_password = ocr
+
+;[users]
+# disable user signup / registration
+;allow_sign_up = false
+
+# Set to true to automatically assign new users to the default organization (id 1)
+;auto_assign_org = true
+
+# Default role new users will be automatically assigned (if disabled above is set to true)
+;auto_assign_org_role = Viewer
+
+#################################### SMTP / Emailing ##########################
+;[smtp]
+;enabled = false
+;host = localhost:25
+;user =
+;password =
+;cert_file =
+;key_file =
+;skip_verify = false
+;from_address = admin@grafana.localhost
+
+;[emails]
+;welcome_email_on_sign_up = false
+
diff --git a/third-party/nwaku/apps/liteprotocoltester/monitoring/prometheus-config.yml b/third-party/nwaku/apps/liteprotocoltester/monitoring/prometheus-config.yml
new file mode 100644
index 0000000..d04eaf0
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/monitoring/prometheus-config.yml
@@ -0,0 +1,35 @@
+global:
+ scrape_interval: 15s
+ evaluation_interval: 15s
+ external_labels:
+ monitor: "Monitoring"
+
+scrape_configs:
+ - job_name: "liteprotocoltester"
+ static_configs:
+ - targets: ["liteprotocoltester-publishernode-1:8003",
+ "liteprotocoltester-publishernode-2:8003",
+ "liteprotocoltester-publishernode-3:8003",
+ "liteprotocoltester-publishernode-4:8003",
+ "liteprotocoltester-publishernode-5:8003",
+ "liteprotocoltester-publishernode-6:8003",
+ "liteprotocoltester-receivernode-1:8003",
+ "liteprotocoltester-receivernode-2:8003",
+ "liteprotocoltester-receivernode-3:8003",
+ "liteprotocoltester-receivernode-4:8003",
+ "liteprotocoltester-receivernode-5:8003",
+ "liteprotocoltester-receivernode-6:8003",
+ "publishernode:8003",
+ "publishernode-1:8003",
+ "publishernode-2:8003",
+ "publishernode-3:8003",
+ "publishernode-4:8003",
+ "publishernode-5:8003",
+ "publishernode-6:8003",
+ "receivernode:8003",
+ "receivernode-1:8003",
+ "receivernode-2:8003",
+ "receivernode-3:8003",
+ "receivernode-4:8003",
+ "receivernode-5:8003",
+ "receivernode-6:8003",]
diff --git a/third-party/nwaku/apps/liteprotocoltester/nim.cfg b/third-party/nwaku/apps/liteprotocoltester/nim.cfg
new file mode 100644
index 0000000..2231f2e
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/nim.cfg
@@ -0,0 +1,4 @@
+-d:chronicles_line_numbers
+-d:chronicles_runtime_filtering:on
+-d:discv5_protocol_id:d5waku
+path = "../.."
diff --git a/third-party/nwaku/apps/liteprotocoltester/publisher.nim b/third-party/nwaku/apps/liteprotocoltester/publisher.nim
new file mode 100644
index 0000000..d803147
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/publisher.nim
@@ -0,0 +1,272 @@
+import
+ std/[strformat, sysrand, random, strutils, sequtils],
+ system/ansi_c,
+ chronicles,
+ chronos,
+ chronos/timer as chtimer,
+ stew/byteutils,
+ results,
+ json_serialization as js
+import
+ waku/[
+ common/logging,
+ waku_node,
+ node/peer_manager,
+ waku_core,
+ waku_lightpush/client,
+ waku_lightpush/common,
+ common/utils/parse_size_units,
+ ],
+ ./tester_config,
+ ./tester_message,
+ ./lpt_metrics,
+ ./diagnose_connections,
+ ./service_peer_management,
+ ./publisher_base,
+ ./legacy_publisher,
+ ./v3_publisher
+
+randomize()
+
+type SizeRange* = tuple[min: uint64, max: uint64]
+
+var RANDOM_PAYLOAD {.threadvar.}: seq[byte]
+RANDOM_PAYLOAD = urandom(1024 * 1024)
+ # 1MiB of random payload to be used to extend message
+
+proc prepareMessage(
+ sender: string,
+ messageIndex, numMessages: uint32,
+ startedAt: TimeStamp,
+ prevMessageAt: var Timestamp,
+ contentTopic: ContentTopic,
+ size: SizeRange,
+): (WakuMessage, uint64) =
+ var renderSize = rand(size.min .. size.max)
+ let current = getNowInNanosecondTime()
+ let payload = ProtocolTesterMessage(
+ sender: sender,
+ index: messageIndex,
+ count: numMessages,
+ startedAt: startedAt,
+ sinceStart: current - startedAt,
+ sincePrev: current - prevMessageAt,
+ size: renderSize,
+ )
+
+ prevMessageAt = current
+
+ let text = js.Json.encode(payload)
+ let contentPayload = toBytes(text & " \0")
+
+ if renderSize < len(contentPayload).uint64:
+ renderSize = len(contentPayload).uint64
+
+ let finalPayload =
+ concat(contentPayload, RANDOM_PAYLOAD[0 .. renderSize - len(contentPayload).uint64])
+ let message = WakuMessage(
+ payload: finalPayload, # content of the message
+ contentTopic: contentTopic, # content topic to publish to
+ ephemeral: true, # tell store nodes to not store it
+ timestamp: current, # current timestamp
+ )
+
+ return (message, renderSize)
+
+var sentMessages {.threadvar.}: OrderedTable[uint32, tuple[hash: string, relayed: bool]]
+var failedToSendCause {.threadvar.}: Table[string, uint32]
+var failedToSendCount {.threadvar.}: uint32
+var numMessagesToSend {.threadvar.}: uint32
+var messagesSent {.threadvar.}: uint32
+var noOfServicePeerSwitches {.threadvar.}: uint32
+
+proc reportSentMessages() =
+ let report = catch:
+ """*----------------------------------------*
+| Service Peer Switches: {noOfServicePeerSwitches:>15} |
+*----------------------------------------*
+| Expected | Sent | Failed |
+|{numMessagesToSend+failedToSendCount:>11} |{messagesSent:>11} |{failedToSendCount:>11} |
+*----------------------------------------*""".fmt()
+
+ if report.isErr:
+ echo "Error while printing statistics"
+ else:
+ echo report.get()
+
+ echo "*--------------------------------------------------------------------------------------------------*"
+ echo "| Failure cause | count |"
+ for (cause, count) in failedToSendCause.pairs:
+ echo fmt"|{cause:<87}|{count:>10}|"
+ echo "*--------------------------------------------------------------------------------------------------*"
+
+ echo "*--------------------------------------------------------------------------------------------------*"
+ echo "| Index | Relayed | Hash |"
+ for (index, info) in sentMessages.pairs:
+ echo fmt"|{index+1:>10}|{info.relayed:<9}| {info.hash:<76}|"
+ echo "*--------------------------------------------------------------------------------------------------*"
+ # evere sent message hash should logged once
+ sentMessages.clear()
+
+proc publishMessages(
+ wakuNode: WakuNode,
+ publisher: PublisherBase,
+ servicePeer: RemotePeerInfo,
+ lightpushPubsubTopic: PubsubTopic,
+ lightpushContentTopic: ContentTopic,
+ numMessages: uint32,
+ messageSizeRange: SizeRange,
+ messageInterval: Duration,
+ preventPeerSwitch: bool,
+) {.async.} =
+ var actualServicePeer = servicePeer
+ let startedAt = getNowInNanosecondTime()
+ var prevMessageAt = startedAt
+ var renderMsgSize = messageSizeRange
+ # sets some default of min max message size to avoid conflict with meaningful payload size
+ renderMsgSize.min = max(1024.uint64, renderMsgSize.min) # do not use less than 1KB
+ renderMsgSize.max = max(2048.uint64, renderMsgSize.max) # minimum of max is 2KB
+ renderMsgSize.min = min(renderMsgSize.min, renderMsgSize.max)
+ renderMsgSize.max = max(renderMsgSize.min, renderMsgSize.max)
+
+ const maxFailedPush = 3
+ var noFailedPush = 0
+ var noFailedServiceNodeSwitches = 0
+
+ let selfPeerId = $wakuNode.switch.peerInfo.peerId
+ failedToSendCount = 0
+ numMessagesToSend = if numMessages == 0: uint32.high else: numMessages
+ messagesSent = 0
+
+ while messagesSent < numMessagesToSend:
+ let (message, msgSize) = prepareMessage(
+ selfPeerId,
+ messagesSent + 1,
+ numMessagesToSend,
+ startedAt,
+ prevMessageAt,
+ lightpushContentTopic,
+ renderMsgSize,
+ )
+
+ let publishStartTime = Moment.now()
+
+ let wlpRes = await publisher.send(lightpushPubsubTopic, message, actualServicePeer)
+
+ let publishDuration = Moment.now() - publishStartTime
+
+ let msgHash = computeMessageHash(lightpushPubsubTopic, message).to0xHex
+
+ if wlpRes.isOk():
+ lpt_publish_duration_seconds.observe(publishDuration.milliseconds.float / 1000)
+
+ sentMessages[messagesSent] = (hash: msgHash, relayed: true)
+ notice "published message using lightpush",
+ index = messagesSent + 1,
+ count = numMessagesToSend,
+ size = msgSize,
+ pubsubTopic = lightpushPubsubTopic,
+ hash = msgHash
+ inc(messagesSent)
+ lpt_publisher_sent_messages_count.inc()
+ lpt_publisher_sent_bytes.inc(amount = msgSize.int64)
+ if noFailedPush > 0:
+ noFailedPush -= 1
+ else:
+ sentMessages[messagesSent] = (hash: msgHash, relayed: false)
+ failedToSendCause.mgetOrPut(wlpRes.error, 1).inc()
+ error "failed to publish message using lightpush",
+ err = wlpRes.error, hash = msgHash
+ inc(failedToSendCount)
+ lpt_publisher_failed_messages_count.inc(labelValues = [wlpRes.error])
+ if not wlpRes.error.toLower().contains("dial"):
+ # retry sending after shorter wait
+ await sleepAsync(2.seconds)
+ continue
+ else:
+ noFailedPush += 1
+ lpt_service_peer_failure_count.inc(
+ labelValues = ["publisher", actualServicePeer.getAgent()]
+ )
+ if not preventPeerSwitch and noFailedPush > maxFailedPush:
+ info "Max push failure limit reached, Try switching peer."
+ let peerOpt = selectRandomServicePeer(
+ wakuNode.peerManager, some(actualServicePeer), WakuLightPushCodec
+ )
+ if peerOpt.isOk():
+ actualServicePeer = peerOpt.get()
+
+ info "New service peer in use",
+ codec = lightpushPubsubTopic,
+ peer = constructMultiaddrStr(actualServicePeer)
+
+ noFailedPush = 0
+ noOfServicePeerSwitches += 1
+ lpt_change_service_peer_count.inc(labelValues = ["publisher"])
+ continue # try again with new peer without delay
+ else:
+ error "Failed to find new service peer. Exiting."
+ noFailedServiceNodeSwitches += 1
+ break
+
+ await sleepAsync(messageInterval)
+
+proc setupAndPublish*(
+ wakuNode: WakuNode, conf: LiteProtocolTesterConf, servicePeer: RemotePeerInfo
+) =
+ var publisher: PublisherBase
+ if conf.lightpushVersion == LightpushVersion.LEGACY:
+ info "Using legacy lightpush protocol for publishing messages"
+ publisher = LegacyPublisher.new(wakuNode)
+ else:
+ info "Using lightpush v3 protocol for publishing messages"
+ publisher = V3Publisher.new(wakuNode)
+
+ # give some time to receiver side to set up
+ let waitTillStartTesting = conf.startPublishingAfter.seconds
+
+ let parsedMinMsgSize = parseMsgSize(conf.minTestMessageSize).valueOr:
+ error "failed to parse 'min-test-msg-size' param: ", error = error
+ return
+
+ let parsedMaxMsgSize = parseMsgSize(conf.maxTestMessageSize).valueOr:
+ error "failed to parse 'max-test-msg-size' param: ", error = error
+ return
+
+ info "Sending test messages in", wait = waitTillStartTesting
+ waitFor sleepAsync(waitTillStartTesting)
+
+ info "Start sending messages to service node using lightpush"
+
+ sentMessages.sort(system.cmp)
+
+ let interval = secs(60)
+ var printStats: CallbackFunc
+
+ printStats = CallbackFunc(
+ proc(udata: pointer) {.gcsafe.} =
+ reportSentMessages()
+
+ if messagesSent >= numMessagesToSend:
+ info "All messages are sent. Exiting."
+
+ ## for gracefull shutdown through signal hooks
+ discard c_raise(ansi_c.SIGTERM)
+ else:
+ discard setTimer(Moment.fromNow(interval), printStats)
+ )
+
+ discard setTimer(Moment.fromNow(interval), printStats)
+
+ # Start maintaining subscription
+ asyncSpawn publishMessages(
+ wakuNode,
+ publisher,
+ servicePeer,
+ conf.getPubsubTopic(),
+ conf.contentTopics[0],
+ conf.numMessages,
+ (min: parsedMinMsgSize, max: parsedMaxMsgSize),
+ conf.messageInterval.milliseconds,
+ conf.fixedServicePeer,
+ )
diff --git a/third-party/nwaku/apps/liteprotocoltester/publisher_base.nim b/third-party/nwaku/apps/liteprotocoltester/publisher_base.nim
new file mode 100644
index 0000000..de88d82
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/publisher_base.nim
@@ -0,0 +1,14 @@
+import chronos, results
+import waku/[waku_node, waku_core]
+
+type PublisherBase* = ref object of RootObj
+ wakuNode*: WakuNode
+
+method send*(
+ self: PublisherBase,
+ topic: PubsubTopic,
+ message: WakuMessage,
+ servicePeer: RemotePeerInfo,
+): Future[Result[void, string]] {.base, async.} =
+ discard
+ # when error it must return original error desc due the text is used for distinction between error types in metrics.
diff --git a/third-party/nwaku/apps/liteprotocoltester/receiver.nim b/third-party/nwaku/apps/liteprotocoltester/receiver.nim
new file mode 100644
index 0000000..f0f41b1
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/receiver.nim
@@ -0,0 +1,182 @@
+## Example showing how a resource restricted client may
+## subscribe to messages without relay
+
+import
+ std/options,
+ system/ansi_c,
+ chronicles,
+ chronos,
+ chronos/timer as chtimer,
+ stew/byteutils,
+ results,
+ serialization,
+ json_serialization as js
+
+import
+ waku/[
+ common/logging,
+ node/peer_manager,
+ waku_node,
+ waku_core,
+ waku_filter_v2/client,
+ waku_filter_v2/common,
+ waku_core/multiaddrstr,
+ ],
+ ./tester_config,
+ ./tester_message,
+ ./statistics,
+ ./diagnose_connections,
+ ./service_peer_management,
+ ./lpt_metrics
+
+var actualFilterPeer {.threadvar.}: RemotePeerInfo
+
+proc unsubscribe(
+ wakuNode: WakuNode, filterPubsubTopic: PubsubTopic, filterContentTopic: ContentTopic
+) {.async.} =
+ notice "unsubscribing from filter"
+ let unsubscribeRes = await wakuNode.wakuFilterClient.unsubscribe(
+ actualFilterPeer, filterPubsubTopic, @[filterContentTopic]
+ )
+ if unsubscribeRes.isErr:
+ notice "unsubscribe request failed", err = unsubscribeRes.error
+ else:
+ notice "unsubscribe request successful"
+
+proc maintainSubscription(
+ wakuNode: WakuNode,
+ filterPubsubTopic: PubsubTopic,
+ filterContentTopic: ContentTopic,
+ preventPeerSwitch: bool,
+) {.async.} =
+ const maxFailedSubscribes = 3
+ const maxFailedServiceNodeSwitches = 10
+ var noFailedSubscribes = 0
+ var noFailedServiceNodeSwitches = 0
+ var isFirstPingOnNewPeer = true
+ while true:
+ info "maintaining subscription at", peer = constructMultiaddrStr(actualFilterPeer)
+ # First use filter-ping to check if we have an active subscription
+ let pingRes = await wakuNode.wakuFilterClient.ping(actualFilterPeer)
+ if pingRes.isErr():
+ if isFirstPingOnNewPeer == false:
+ # Very first ping expected to fail as we have not yet subscribed at all
+ lpt_receiver_lost_subscription_count.inc()
+ isFirstPingOnNewPeer = false
+ # No subscription found. Let's subscribe.
+ error "ping failed.", err = pingRes.error
+ trace "no subscription found. Sending subscribe request"
+
+ let subscribeRes = await wakuNode.filterSubscribe(
+ some(filterPubsubTopic), filterContentTopic, actualFilterPeer
+ )
+
+ if subscribeRes.isErr():
+ noFailedSubscribes += 1
+ lpt_service_peer_failure_count.inc(
+ labelValues = ["receiver", actualFilterPeer.getAgent()]
+ )
+ error "Subscribe request failed.",
+ err = subscribeRes.error,
+ peer = actualFilterPeer,
+ failCount = noFailedSubscribes
+
+ # TODO: disconnet from failed actualFilterPeer
+ # asyncSpawn(wakuNode.peerManager.switch.disconnect(p))
+ # wakunode.peerManager.peerStore.delete(actualFilterPeer)
+
+ if noFailedSubscribes < maxFailedSubscribes:
+ await sleepAsync(2.seconds) # Wait a bit before retrying
+ continue
+ elif not preventPeerSwitch:
+ let peerOpt = selectRandomServicePeer(
+ wakuNode.peerManager, some(actualFilterPeer), WakuFilterSubscribeCodec
+ )
+ if peerOpt.isOk():
+ actualFilterPeer = peerOpt.get()
+
+ info "Found new peer for codec",
+ codec = filterPubsubTopic, peer = constructMultiaddrStr(actualFilterPeer)
+
+ noFailedSubscribes = 0
+ lpt_change_service_peer_count.inc(labelValues = ["receiver"])
+ isFirstPingOnNewPeer = true
+ continue # try again with new peer without delay
+ else:
+ error "Failed to find new service peer. Exiting."
+ noFailedServiceNodeSwitches += 1
+ break
+ else:
+ if noFailedSubscribes > 0:
+ noFailedSubscribes -= 1
+
+ notice "subscribe request successful."
+ else:
+ info "subscription is live."
+
+ await sleepAsync(30.seconds) # Subscription maintenance interval
+
+proc setupAndListen*(
+ wakuNode: WakuNode, conf: LiteProtocolTesterConf, servicePeer: RemotePeerInfo
+) =
+ if isNil(wakuNode.wakuFilterClient):
+ # if we have not yet initialized lightpush client, then do it as the only way we can get here is
+ # by having a service peer discovered.
+ waitFor wakuNode.mountFilterClient()
+
+ info "Start receiving messages to service node using filter",
+ servicePeer = servicePeer
+
+ var stats: PerPeerStatistics
+ actualFilterPeer = servicePeer
+
+ let pushHandler = proc(
+ pubsubTopic: PubsubTopic, message: WakuMessage
+ ): Future[void] {.async, closure.} =
+ let payloadStr = string.fromBytes(message.payload)
+ let testerMessage = js.Json.decode(payloadStr, ProtocolTesterMessage)
+ let msgHash = computeMessageHash(pubsubTopic, message).to0xHex
+
+ stats.addMessage(testerMessage.sender, testerMessage, msgHash)
+
+ notice "message received",
+ index = testerMessage.index,
+ count = testerMessage.count,
+ startedAt = $testerMessage.startedAt,
+ sinceStart = $testerMessage.sinceStart,
+ sincePrev = $testerMessage.sincePrev,
+ size = $testerMessage.size,
+ pubsubTopic = pubsubTopic,
+ hash = msgHash
+
+ wakuNode.wakuFilterClient.registerPushHandler(pushHandler)
+
+ let interval = millis(20000)
+ var printStats: CallbackFunc
+
+ # calculate max wait after the last known message arrived before exiting
+ # 20% of expected messages times the expected interval but capped to 10min
+ let maxWaitForLastMessage: Duration =
+ min(conf.messageInterval.milliseconds * (conf.numMessages div 5), 10.minutes)
+
+ printStats = CallbackFunc(
+ proc(udata: pointer) {.gcsafe.} =
+ stats.echoStats()
+
+ if conf.numMessages > 0 and
+ waitFor stats.checkIfAllMessagesReceived(maxWaitForLastMessage):
+ waitFor unsubscribe(wakuNode, conf.getPubsubTopic(), conf.contentTopics[0])
+ info "All messages received. Exiting."
+
+ ## for gracefull shutdown through signal hooks
+ discard c_raise(ansi_c.SIGTERM)
+ else:
+ discard setTimer(Moment.fromNow(interval), printStats)
+ )
+
+ discard setTimer(Moment.fromNow(interval), printStats)
+
+ # Start maintaining subscription
+ asyncSpawn maintainSubscription(
+ wakuNode, conf.getPubsubTopic(), conf.contentTopics[0], conf.fixedServicePeer
+ )
diff --git a/third-party/nwaku/apps/liteprotocoltester/run_service_node.sh b/third-party/nwaku/apps/liteprotocoltester/run_service_node.sh
new file mode 100755
index 0000000..07fdbe9
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/run_service_node.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+echo "I am a service node"
+IP=$(ip a | grep "inet " | grep -Fv 127.0.0.1 | sed 's/.*inet \([^/]*\).*/\1/')
+
+echo "Service node IP: ${IP}"
+
+if [ -n "${SHARD}" ]; then
+ SHARD=--shard="${SHARD}"
+else
+ SHARD=--shard="0"
+fi
+
+if [ -n "${CLUSTER_ID}" ]; then
+ CLUSTER_ID=--cluster-id="${CLUSTER_ID}"
+fi
+
+echo "STANDALONE: ${STANDALONE}"
+
+if [ -z "${STANDALONE}" ]; then
+
+ RETRIES=${RETRIES:=20}
+
+ while [ -z "${BOOTSTRAP_ENR}" ] && [ ${RETRIES} -ge 0 ]; do
+ BOOTSTRAP_ENR=$(wget -qO- http://bootstrap:8645/debug/v1/info --header='Content-Type:application/json' 2> /dev/null | sed 's/.*"enrUri":"\([^"]*\)".*/\1/');
+ echo "Bootstrap node not ready, retrying (retries left: ${RETRIES})"
+ sleep 3
+ RETRIES=$(( $RETRIES - 1 ))
+ done
+
+ if [ -z "${BOOTSTRAP_ENR}" ]; then
+ echo "Could not get BOOTSTRAP_ENR and none provided. Failing"
+ exit 1
+ fi
+
+ echo "Using bootstrap node: ${BOOTSTRAP_ENR}"
+
+fi
+
+
+exec /usr/bin/wakunode\
+ --relay=true\
+ --filter=true\
+ --lightpush=true\
+ --store=false\
+ --rest=true\
+ --rest-admin=true\
+ --rest-private=true\
+ --rest-address=0.0.0.0\
+ --rest-allow-origin="*"\
+ --keep-alive=true\
+ --max-connections=300\
+ --dns-discovery=true\
+ --discv5-discovery=true\
+ --discv5-enr-auto-update=True\
+ --discv5-bootstrap-node=${BOOTSTRAP_ENR}\
+ --log-level=INFO\
+ --metrics-server=True\
+ --metrics-server-port=8003\
+ --metrics-server-address=0.0.0.0\
+ --nat=extip:${IP}\
+ ${SHARD}\
+ ${CLUSTER_ID}
diff --git a/third-party/nwaku/apps/liteprotocoltester/run_tester_node.sh b/third-party/nwaku/apps/liteprotocoltester/run_tester_node.sh
new file mode 100755
index 0000000..3c2d60e
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/run_tester_node.sh
@@ -0,0 +1,161 @@
+#!/bin/sh
+
+#set -x
+
+if test -f .env; then
+ echo "Using .env file"
+ . $(pwd)/.env
+fi
+
+
+echo "I am a lite-protocol-tester node"
+
+BINARY_PATH=$1
+
+if [ ! -x "${BINARY_PATH}" ]; then
+ echo "Invalid binary path '${BINARY_PATH}'. Failing"
+ exit 1
+fi
+
+if [ "${2}" = "--help" ]; then
+ echo "You might want to check nwaku/apps/liteprotocoltester/README.md"
+ exec "${BINARY_PATH}" --help
+ exit 0
+fi
+
+FUNCTION=$2
+if [ "${FUNCTION}" = "SENDER" ]; then
+ FUNCTION="--test-func=SENDER --lightpush-version=LEGACY"
+ SERVICENAME=lightpush-service
+fi
+
+if [ "${FUNCTION}" = "SENDERV3" ]; then
+ FUNCTION="--test-func=SENDER --lightpush-version=V3"
+ SERVICENAME=lightpush-service
+fi
+
+if [ "${FUNCTION}" = "RECEIVER" ]; then
+ FUNCTION=--test-func=RECEIVER
+ SERVICENAME=filter-service
+fi
+
+SERIVCE_NODE_ADDR=$3
+if [ -z "${SERIVCE_NODE_ADDR}" ]; then
+ echo "Service node peer_id provided. Failing"
+ exit 1
+fi
+
+SELECTOR=$4
+if [ -z "${SELECTOR}" ] || [ "${SELECTOR}" = "SERVICE" ]; then
+ SERVICE_NODE_DIRECT=true
+elif [ "${SELECTOR}" = "BOOTSTRAP" ]; then
+ SERVICE_NODE_DIRECT=false
+else
+ echo "Invalid selector '${SELECTOR}'. Failing"
+ exit 1
+fi
+
+DO_DETECT_SERVICENODE=0
+
+if [ "${SERIVCE_NODE_ADDR}" = "servicenode" ]; then
+ DO_DETECT_SERVICENODE=1
+ SERIVCE_NODE_ADDR=""
+ SERVICENAME=servicenode
+fi
+
+if [ "${SERIVCE_NODE_ADDR}" = "waku-sim" ]; then
+ DO_DETECT_SERVICENODE=1
+ SERIVCE_NODE_ADDR=""
+ MY_EXT_IP=$(ip a | grep "inet " | grep -Fv 127.0.0.1 | sed 's/.*inet \([^/]*\).*/\1/')
+else
+ MY_EXT_IP=$(wget -qO- --no-check-certificate https://api4.ipify.org)
+fi
+
+
+if [ $DO_DETECT_SERVICENODE -eq 1 ]; then
+ RETRIES=${RETRIES:=20}
+
+ while [ -z "${SERIVCE_NODE_ADDR}" ] && [ ${RETRIES} -ge 0 ]; do
+ SERVICE_DEBUG_INFO=$(wget -qO- http://${SERVICENAME}:8645/debug/v1/info --header='Content-Type:application/json' 2> /dev/null);
+ echo "SERVICE_DEBUG_INFO: ${SERVICE_DEBUG_INFO}"
+
+ SERIVCE_NODE_ADDR=$(wget -qO- http://${SERVICENAME}:8645/debug/v1/info --header='Content-Type:application/json' 2> /dev/null | sed 's/.*"listenAddresses":\["\([^"]*\)".*/\1/');
+ echo "Service node not ready, retrying (retries left: ${RETRIES})"
+ sleep 3
+ RETRIES=$(( $RETRIES - 1 ))
+ done
+
+fi
+
+if [ -z "${SERIVCE_NODE_ADDR}" ]; then
+ echo "Could not get SERIVCE_NODE_ADDR and none provided. Failing"
+ exit 1
+fi
+
+if $SERVICE_NODE_DIRECT; then
+ FULL_NODE=--service-node="${SERIVCE_NODE_ADDR} --fixed-service-peer"
+else
+ FULL_NODE=--bootstrap-node="${SERIVCE_NODE_ADDR}"
+fi
+
+if [ -n "${SHARD}" ]; then
+ SHARD=--shard="${SHARD}"
+else
+ SHARD=--shard="0"
+fi
+
+if [ -n "${CONTENT_TOPIC}" ]; then
+ CONTENT_TOPIC=--content-topic="${CONTENT_TOPIC}"
+fi
+
+if [ -n "${CLUSTER_ID}" ]; then
+ CLUSTER_ID=--cluster-id="${CLUSTER_ID}"
+fi
+
+if [ -n "${START_PUBLISHING_AFTER_SECS}" ]; then
+ START_PUBLISHING_AFTER_SECS=--start-publishing-after="${START_PUBLISHING_AFTER_SECS}"
+fi
+
+if [ -n "${MIN_MESSAGE_SIZE}" ]; then
+ MIN_MESSAGE_SIZE=--min-test-msg-size="${MIN_MESSAGE_SIZE}"
+fi
+
+if [ -n "${MAX_MESSAGE_SIZE}" ]; then
+ MAX_MESSAGE_SIZE=--max-test-msg-size="${MAX_MESSAGE_SIZE}"
+fi
+
+
+if [ -n "${NUM_MESSAGES}" ]; then
+ NUM_MESSAGES=--num-messages="${NUM_MESSAGES}"
+fi
+
+if [ -n "${MESSAGE_INTERVAL_MILLIS}" ]; then
+ MESSAGE_INTERVAL_MILLIS=--message-interval="${MESSAGE_INTERVAL_MILLIS}"
+fi
+
+if [ -n "${LOG_LEVEL}" ]; then
+ LOG_LEVEL=--log-level=${LOG_LEVEL}
+else
+ LOG_LEVEL=--log-level=INFO
+fi
+
+echo "Running binary: ${BINARY_PATH}"
+echo "Tester node: ${FUNCTION}"
+echo "Using service node: ${SERIVCE_NODE_ADDR}"
+echo "My external IP: ${MY_EXT_IP}"
+
+exec "${BINARY_PATH}"\
+ --nat=extip:${MY_EXT_IP}\
+ --test-peers\
+ ${LOG_LEVEL}\
+ ${FULL_NODE}\
+ ${MESSAGE_INTERVAL_MILLIS}\
+ ${NUM_MESSAGES}\
+ ${SHARD}\
+ ${CONTENT_TOPIC}\
+ ${CLUSTER_ID}\
+ ${FUNCTION}\
+ ${START_PUBLISHING_AFTER_SECS}\
+ ${MIN_MESSAGE_SIZE}\
+ ${MAX_MESSAGE_SIZE}
+ # --config-file=config.toml\
diff --git a/third-party/nwaku/apps/liteprotocoltester/run_tester_node_at_infra.sh b/third-party/nwaku/apps/liteprotocoltester/run_tester_node_at_infra.sh
new file mode 100644
index 0000000..db26eb0
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/run_tester_node_at_infra.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+#set -x
+#echo "$@"
+
+if test -f .env; then
+ echo "Using .env file"
+ . $(pwd)/.env
+fi
+
+
+echo "I am a lite-protocol-tester node"
+
+BINARY_PATH=$1
+
+if [ ! -x "${BINARY_PATH}" ]; then
+ echo "Invalid binary path '${BINARY_PATH}'. Failing"
+ exit 1
+fi
+
+if [ "${2}" = "--help" ]; then
+ echo "You might want to check nwaku/apps/liteprotocoltester/README.md"
+ exec "${BINARY_PATH}" --help
+ exit 0
+fi
+
+FUNCTION=$2
+if [ "${FUNCTION}" = "SENDER" ]; then
+ FUNCTION="--test-func=SENDER --lightpush-version=LEGACY"
+ SERIVCE_NODE_ADDR=${LIGHTPUSH_SERVICE_PEER:-${LIGHTPUSH_BOOTSTRAP:-}}
+ NODE_ARG=${LIGHTPUSH_SERVICE_PEER:+--service-node="${LIGHTPUSH_SERVICE_PEER}"}
+ NODE_ARG=${NODE_ARG:---bootstrap-node="${LIGHTPUSH_BOOTSTRAP}"}
+ METRICS_PORT=--metrics-port="${PUBLISHER_METRICS_PORT:-8003}"
+fi
+
+if [ "${FUNCTION}" = "SENDERV3" ]; then
+ FUNCTION="--test-func=SENDER --lightpush-version=V3"
+ SERIVCE_NODE_ADDR=${LIGHTPUSH_SERVICE_PEER:-${LIGHTPUSH_BOOTSTRAP:-}}
+ NODE_ARG=${LIGHTPUSH_SERVICE_PEER:+--service-node="${LIGHTPUSH_SERVICE_PEER}"}
+ NODE_ARG=${NODE_ARG:---bootstrap-node="${LIGHTPUSH_BOOTSTRAP}"}
+ METRICS_PORT=--metrics-port="${PUBLISHER_METRICS_PORT:-8003}"
+fi
+
+if [ "${FUNCTION}" = "RECEIVER" ]; then
+ FUNCTION=--test-func=RECEIVER
+ SERIVCE_NODE_ADDR=${FILTER_SERVICE_PEER:-${FILTER_BOOTSTRAP:-}}
+ NODE_ARG=${FILTER_SERVICE_PEER:+--service-node="${FILTER_SERVICE_PEER}"}
+ NODE_ARG=${NODE_ARG:---bootstrap-node="${FILTER_BOOTSTRAP}"}
+ METRICS_PORT=--metrics-port="${RECEIVER_METRICS_PORT:-8003}"
+fi
+
+if [ -z "${SERIVCE_NODE_ADDR}" ]; then
+ echo "Service/Bootsrap node peer_id or enr is not provided. Failing"
+ exit 1
+fi
+
+MY_EXT_IP=$(wget -qO- --no-check-certificate https://api4.ipify.org)
+
+if [ -n "${SHARD}" ]; then
+ SHARD=--shard="${SHARD}"
+else
+ SHARD=--shard="0"
+fi
+
+if [ -n "${CONTENT_TOPIC}" ]; then
+ CONTENT_TOPIC=--content-topic="${CONTENT_TOPIC}"
+fi
+
+if [ -n "${CLUSTER_ID}" ]; then
+ CLUSTER_ID=--cluster-id="${CLUSTER_ID}"
+fi
+
+if [ -n "${START_PUBLISHING_AFTER_SECS}" ]; then
+ START_PUBLISHING_AFTER_SECS=--start-publishing-after="${START_PUBLISHING_AFTER_SECS}"
+fi
+
+if [ -n "${MIN_MESSAGE_SIZE}" ]; then
+ MIN_MESSAGE_SIZE=--min-test-msg-size="${MIN_MESSAGE_SIZE}"
+fi
+
+if [ -n "${MAX_MESSAGE_SIZE}" ]; then
+ MAX_MESSAGE_SIZE=--max-test-msg-size="${MAX_MESSAGE_SIZE}"
+fi
+
+
+if [ -n "${NUM_MESSAGES}" ]; then
+ NUM_MESSAGES=--num-messages="${NUM_MESSAGES}"
+fi
+
+if [ -n "${MESSAGE_INTERVAL_MILLIS}" ]; then
+ MESSAGE_INTERVAL_MILLIS=--message-interval="${MESSAGE_INTERVAL_MILLIS}"
+fi
+
+if [ -n "${LOG_LEVEL}" ]; then
+ LOG_LEVEL=--log-level=${LOG_LEVEL}
+else
+ LOG_LEVEL=--log-level=INFO
+fi
+
+echo "Running binary: ${BINARY_PATH}"
+echo "Node function is: ${FUNCTION}"
+echo "Using service/bootstrap node as: ${NODE_ARG}"
+echo "My external IP: ${MY_EXT_IP}"
+
+exec "${BINARY_PATH}"\
+ --nat=extip:${MY_EXT_IP}\
+ --test-peers\
+ ${LOG_LEVEL}\
+ ${NODE_ARG}\
+ ${MESSAGE_INTERVAL_MILLIS}\
+ ${NUM_MESSAGES}\
+ ${SHARD}\
+ ${CONTENT_TOPIC}\
+ ${CLUSTER_ID}\
+ ${FUNCTION}\
+ ${START_PUBLISHING_AFTER_SECS}\
+ ${MIN_MESSAGE_SIZE}\
+ ${MAX_MESSAGE_SIZE}\
+ ${METRICS_PORT}
diff --git a/third-party/nwaku/apps/liteprotocoltester/run_tester_node_on_fleet.sh b/third-party/nwaku/apps/liteprotocoltester/run_tester_node_on_fleet.sh
new file mode 100644
index 0000000..533f5b1
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/run_tester_node_on_fleet.sh
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+#set -x
+#echo "$@"
+
+if test -f .env; then
+ echo "Using .env file"
+ . $(pwd)/.env
+fi
+
+
+echo "I am a lite-protocol-tester node"
+
+BINARY_PATH=$1
+
+if [ ! -x "${BINARY_PATH}" ]; then
+ echo "Invalid binary path '${BINARY_PATH}'. Failing"
+ exit 1
+fi
+
+if [ "${2}" = "--help" ]; then
+ echo "You might want to check nwaku/apps/liteprotocoltester/README.md"
+ exec "${BINARY_PATH}" --help
+ exit 0
+fi
+
+FUNCTION=$2
+if [ "${FUNCTION}" = "SENDER" ]; then
+ FUNCTION="--test-func=SENDER --lightpush-version=LEGACY"
+ SERIVCE_NODE_ADDR=${LIGHTPUSH_SERVICE_PEER:-${LIGHTPUSH_BOOTSTRAP:-}}
+ NODE_ARG=${LIGHTPUSH_SERVICE_PEER:+--service-node="${LIGHTPUSH_SERVICE_PEER}"}
+ NODE_ARG=${NODE_ARG:---bootstrap-node="${LIGHTPUSH_BOOTSTRAP}"}
+ METRICS_PORT=--metrics-port="${PUBLISHER_METRICS_PORT:-8003}"
+fi
+
+if [ "${FUNCTION}" = "SENDERV3" ]; then
+ FUNCTION="--test-func=SENDER --lightpush-version=V3"
+ SERIVCE_NODE_ADDR=${LIGHTPUSH_SERVICE_PEER:-${LIGHTPUSH_BOOTSTRAP:-}}
+ NODE_ARG=${LIGHTPUSH_SERVICE_PEER:+--service-node="${LIGHTPUSH_SERVICE_PEER}"}
+ NODE_ARG=${NODE_ARG:---bootstrap-node="${LIGHTPUSH_BOOTSTRAP}"}
+ METRICS_PORT=--metrics-port="${PUBLISHER_METRICS_PORT:-8003}"
+fi
+
+if [ "${FUNCTION}" = "RECEIVER" ]; then
+ FUNCTION=--test-func=RECEIVER
+ SERIVCE_NODE_ADDR=${FILTER_SERVICE_PEER:-${FILTER_BOOTSTRAP:-}}
+ NODE_ARG=${FILTER_SERVICE_PEER:+--service-node="${FILTER_SERVICE_PEER}"}
+ NODE_ARG=${NODE_ARG:---bootstrap-node="${FILTER_BOOTSTRAP}"}
+ METRICS_PORT=--metrics-port="${RECEIVER_METRICS_PORT:-8003}"
+fi
+
+if [ -z "${SERIVCE_NODE_ADDR}" ]; then
+ echo "Service/Bootsrap node peer_id or enr is not provided. Failing"
+ exit 1
+fi
+
+MY_EXT_IP=$(wget -qO- --no-check-certificate https://api4.ipify.org)
+
+if [ -n "${SHARD}" ]; then
+ SHARD=--shard=${SHARD}
+else
+ SHARD=--shard=0
+fi
+
+if [ -n "${CONTENT_TOPIC}" ]; then
+ CONTENT_TOPIC=--content-topic="${CONTENT_TOPIC}"
+fi
+
+if [ -n "${CLUSTER_ID}" ]; then
+ CLUSTER_ID=--cluster-id="${CLUSTER_ID}"
+fi
+
+if [ -n "${START_PUBLISHING_AFTER}" ]; then
+ START_PUBLISHING_AFTER=--start-publishing-after="${START_PUBLISHING_AFTER}"
+fi
+
+if [ -n "${MIN_MESSAGE_SIZE}" ]; then
+ MIN_MESSAGE_SIZE=--min-test-msg-size="${MIN_MESSAGE_SIZE}"
+fi
+
+if [ -n "${MAX_MESSAGE_SIZE}" ]; then
+ MAX_MESSAGE_SIZE=--max-test-msg-size="${MAX_MESSAGE_SIZE}"
+fi
+
+
+if [ -n "${NUM_MESSAGES}" ]; then
+ NUM_MESSAGES=--num-messages="${NUM_MESSAGES}"
+fi
+
+if [ -n "${MESSAGE_INTERVAL_MILLIS}" ]; then
+ MESSAGE_INTERVAL_MILLIS=--message-interval="${MESSAGE_INTERVAL_MILLIS}"
+fi
+
+if [ -n "${LOG_LEVEL}" ]; then
+ LOG_LEVEL=--log-level=${LOG_LEVEL}
+else
+ LOG_LEVEL=--log-level=INFO
+fi
+
+echo "Running binary: ${BINARY_PATH}"
+echo "Node function is: ${FUNCTION}"
+echo "Using service/bootstrap node as: ${NODE_ARG}"
+echo "My external IP: ${MY_EXT_IP}"
+
+exec "${BINARY_PATH}"\
+ --nat=extip:${MY_EXT_IP}\
+ ${LOG_LEVEL}\
+ ${NODE_ARG}\
+ ${MESSAGE_INTERVAL_MILLIS}\
+ ${NUM_MESSAGES}\
+ ${SHARD}\
+ ${CONTENT_TOPIC}\
+ ${CLUSTER_ID}\
+ ${FUNCTION}\
+ ${START_PUBLISHING_AFTER}\
+ ${MIN_MESSAGE_SIZE}\
+ ${MAX_MESSAGE_SIZE}\
+ ${METRICS_PORT}
diff --git a/third-party/nwaku/apps/liteprotocoltester/service_peer_management.nim b/third-party/nwaku/apps/liteprotocoltester/service_peer_management.nim
new file mode 100644
index 0000000..a72daa2
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/service_peer_management.nim
@@ -0,0 +1,223 @@
+{.push raises: [].}
+
+import
+ std/[options, net, sysrand, random, strformat, strutils, sequtils],
+ chronicles,
+ chronos,
+ metrics,
+ libbacktrace,
+ libp2p/crypto/crypto,
+ confutils,
+ libp2p/wire
+
+import
+ ../wakunode2/cli_args,
+ waku/[
+ common/enr,
+ waku_node,
+ node/peer_manager,
+ waku_lightpush/common,
+ waku_relay,
+ waku_filter_v2,
+ waku_peer_exchange/protocol,
+ waku_core/multiaddrstr,
+ waku_core/topics/pubsub_topic,
+ waku_enr/capabilities,
+ waku_enr/sharding,
+ ],
+ ./tester_config,
+ ./diagnose_connections,
+ ./lpt_metrics
+
+logScope:
+ topics = "service peer mgmt"
+
+randomize()
+
+proc translateToRemotePeerInfo*(peerAddress: string): Result[RemotePeerInfo, void] =
+ var peerInfo: RemotePeerInfo
+ var enrRec: enr.Record
+ if enrRec.fromURI(peerAddress):
+ trace "Parsed ENR", enrRec = $enrRec
+ peerInfo = enrRec.toRemotePeerInfo().valueOr:
+ error "failed to convert ENR to RemotePeerInfo", error = error
+ return err()
+ else:
+ peerInfo = parsePeerInfo(peerAddress).valueOr:
+ error "failed to parse node waku peer-exchange peerId", error = error
+ return err()
+
+ return ok(peerInfo)
+
+## To retrieve peers from PeerExchange partner and return one randomly selected one
+## among the ones successfully dialed
+## Note: This is kept for future use.
+proc selectRandomCapablePeer*(
+ pm: PeerManager, codec: string, pubsubTopic: PubsubTopic
+): Future[Option[RemotePeerInfo]] {.async.} =
+ var cap = Capabilities.Filter
+ if codec.contains("lightpush"):
+ cap = Capabilities.Lightpush
+ elif codec.contains("filter"):
+ cap = Capabilities.Filter
+
+ var supportivePeers = pm.switch.peerStore.getPeersByCapability(cap)
+
+ trace "Found supportive peers count", count = supportivePeers.len()
+ trace "Found supportive peers", supportivePeers = $supportivePeers
+ if supportivePeers.len == 0:
+ return none(RemotePeerInfo)
+
+ var found = none(RemotePeerInfo)
+ while found.isNone() and supportivePeers.len > 0:
+ let rndPeerIndex = rand(0 .. supportivePeers.len - 1)
+ let randomPeer = supportivePeers[rndPeerIndex]
+
+ debug "Dialing random peer",
+ idx = $rndPeerIndex, peer = constructMultiaddrStr(randomPeer)
+
+ supportivePeers.delete(rndPeerIndex .. rndPeerIndex)
+
+ let connOpt = pm.dialPeer(randomPeer, codec)
+ if (await connOpt.withTimeout(10.seconds)):
+ if connOpt.value().isSome():
+ found = some(randomPeer)
+ debug "Dialing successful",
+ peer = constructMultiaddrStr(randomPeer), codec = codec
+ else:
+ debug "Dialing failed", peer = constructMultiaddrStr(randomPeer), codec = codec
+ else:
+ debug "Timeout dialing service peer",
+ peer = constructMultiaddrStr(randomPeer), codec = codec
+
+ return found
+
+# Debugging PX gathered peers connectivity
+proc tryCallAllPxPeers*(
+ pm: PeerManager, codec: string, pubsubTopic: PubsubTopic
+): Future[Option[seq[RemotePeerInfo]]] {.async.} =
+ var capability = Capabilities.Filter
+ if codec.contains("lightpush"):
+ capability = Capabilities.Lightpush
+ elif codec.contains("filter"):
+ capability = Capabilities.Filter
+
+ var supportivePeers = pm.switch.peerStore.getPeersByCapability(capability)
+
+ lpt_px_peers.set(supportivePeers.len)
+ debug "Found supportive peers count", count = supportivePeers.len()
+ debug "Found supportive peers", supportivePeers = $supportivePeers
+ if supportivePeers.len == 0:
+ return none(seq[RemotePeerInfo])
+
+ var okPeers: seq[RemotePeerInfo] = @[]
+
+ while supportivePeers.len > 0:
+ let rndPeerIndex = rand(0 .. supportivePeers.len - 1)
+ let randomPeer = supportivePeers[rndPeerIndex]
+
+ debug "Dialing random peer",
+ idx = $rndPeerIndex, peer = constructMultiaddrStr(randomPeer)
+
+ supportivePeers.delete(rndPeerIndex, rndPeerIndex)
+
+ let connOpt = pm.dialPeer(randomPeer, codec)
+ if (await connOpt.withTimeout(10.seconds)):
+ if connOpt.value().isSome():
+ okPeers.add(randomPeer)
+ info "Dialing successful",
+ peer = constructMultiaddrStr(randomPeer),
+ agent = randomPeer.getAgent(),
+ codec = codec
+ lpt_dialed_peers.inc(labelValues = [randomPeer.getAgent()])
+ else:
+ lpt_dial_failures.inc(labelValues = [randomPeer.getAgent()])
+ error "Dialing failed",
+ peer = constructMultiaddrStr(randomPeer),
+ agent = randomPeer.getAgent(),
+ codec = codec
+ else:
+ lpt_dial_failures.inc(labelValues = [randomPeer.getAgent()])
+ error "Timeout dialing service peer",
+ peer = constructMultiaddrStr(randomPeer),
+ agent = randomPeer.getAgent(),
+ codec = codec
+
+ var okPeersStr: string = ""
+ for idx, peer in okPeers:
+ okPeersStr.add(
+ " " & $idx & ". | " & constructMultiaddrStr(peer) & " | agent: " &
+ peer.getAgent() & " | protos: " & $peer.protocols & " | caps: " &
+ $peer.enr.map(getCapabilities) & "\n"
+ )
+ echo "PX returned peers found callable for " & codec & " / " & $capability & ":\n"
+ echo okPeersStr
+
+ return some(okPeers)
+
+proc pxLookupServiceNode*(
+ node: WakuNode, conf: LiteProtocolTesterConf
+): Future[Result[bool, void]] {.async.} =
+ let codec: string = conf.getCodec()
+
+ if node.wakuPeerExchange.isNil():
+ let peerExchangeNode = translateToRemotePeerInfo(conf.bootstrapNode).valueOr:
+ error "Failed to parse bootstrap node - cannot use PeerExchange.",
+ node = conf.bootstrapNode
+ return err()
+ info "PeerExchange node", peer = constructMultiaddrStr(peerExchangeNode)
+ node.peerManager.addServicePeer(peerExchangeNode, WakuPeerExchangeCodec)
+
+ try:
+ await node.mountPeerExchange(some(conf.clusterId))
+ except CatchableError:
+ error "failed to mount waku peer-exchange protocol",
+ error = getCurrentExceptionMsg()
+ return err()
+
+ var trialCount = 5
+ while trialCount > 0:
+ let futPeers = node.fetchPeerExchangePeers(conf.reqPxPeers)
+ if not await futPeers.withTimeout(30.seconds):
+ notice "Cannot get peers from PX", round = 5 - trialCount
+ else:
+ if futPeers.value().isErr():
+ info "PeerExchange reported error", error = futPeers.read().error
+ return err()
+
+ if conf.testPeers:
+ let peersOpt =
+ await tryCallAllPxPeers(node.peerManager, codec, conf.getPubsubTopic())
+ if peersOpt.isSome():
+ info "Found service peers for codec",
+ codec = codec, peer_count = peersOpt.get().len()
+ return ok(peersOpt.get().len > 0)
+ else:
+ let peerOpt =
+ await selectRandomCapablePeer(node.peerManager, codec, conf.getPubsubTopic())
+ if peerOpt.isSome():
+ info "Found service peer for codec", codec = codec, peer = peerOpt.get()
+ return ok(true)
+
+ await sleepAsync(5.seconds)
+ trialCount -= 1
+
+ return err()
+
+var alreadyUsedServicePeers {.threadvar.}: seq[RemotePeerInfo]
+
+## Select service peers by codec from peer store randomly.
+proc selectRandomServicePeer*(
+ pm: PeerManager, actualPeer: Option[RemotePeerInfo], codec: string
+): Result[RemotePeerInfo, void] =
+ if actualPeer.isSome():
+ alreadyUsedServicePeers.add(actualPeer.get())
+
+ let supportivePeers = pm.switch.peerStore.getPeersByProtocol(codec).filterIt(
+ it notin alreadyUsedServicePeers
+ )
+ if supportivePeers.len == 0:
+ return err()
+
+ let rndPeerIndex = rand(0 .. supportivePeers.len - 1)
+ return ok(supportivePeers[rndPeerIndex])
diff --git a/third-party/nwaku/apps/liteprotocoltester/statistics.nim b/third-party/nwaku/apps/liteprotocoltester/statistics.nim
new file mode 100644
index 0000000..8322edd
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/statistics.nim
@@ -0,0 +1,336 @@
+{.push raises: [].}
+
+import
+ std/[sets, tables, sequtils, options, strformat],
+ chronos/timer as chtimer,
+ chronicles,
+ chronos,
+ results,
+ libp2p/peerid
+
+import ./tester_message, ./lpt_metrics
+
+type
+ ArrivalInfo = object
+ arrivedAt: Moment
+ prevArrivedAt: Moment
+ prevIndex: uint32
+
+ MessageInfo = tuple[msg: ProtocolTesterMessage, info: ArrivalInfo]
+ DupStat = tuple[hash: string, dupCount: int, size: uint64]
+
+ StatHelper = object
+ prevIndex: uint32
+ prevArrivedAt: Moment
+ lostIndices: HashSet[uint32]
+ seenIndices: HashSet[uint32]
+ maxIndex: uint32
+ duplicates: OrderedTable[uint32, DupStat]
+
+ Statistics* = object
+ received: Table[uint32, MessageInfo]
+ firstReceivedIdx*: uint32
+ allMessageCount*: uint32
+ receivedMessages*: uint32
+ misorderCount*: uint32
+ lateCount*: uint32
+ duplicateCount*: uint32
+ helper: StatHelper
+
+ PerPeerStatistics* = Table[string, Statistics]
+
+func `$`*(a: Duration): string {.inline.} =
+ ## Original stringify implementation from chronos/timer.nim is not capable of printing 0ns
+ ## Returns string representation of Duration ``a`` as nanoseconds value.
+
+ if a.isZero:
+ return "0ns"
+
+ return chtimer.`$`(a)
+
+proc init*(T: type Statistics, expectedMessageCount: int = 1000): T =
+ result.helper.prevIndex = 0
+ result.helper.maxIndex = 0
+ result.helper.seenIndices.init(expectedMessageCount)
+ result.received = initTable[uint32, MessageInfo](expectedMessageCount)
+ return result
+
+proc addMessage*(
+ self: var Statistics, sender: string, msg: ProtocolTesterMessage, msgHash: string
+) =
+ if self.allMessageCount == 0:
+ self.allMessageCount = msg.count
+ self.firstReceivedIdx = msg.index
+ elif self.allMessageCount != msg.count:
+ error "Message count mismatch at message",
+ index = msg.index, expected = self.allMessageCount, got = msg.count
+
+ let currentArrived: MessageInfo = (
+ msg: msg,
+ info: ArrivalInfo(
+ arrivedAt: Moment.now(),
+ prevArrivedAt: self.helper.prevArrivedAt,
+ prevIndex: self.helper.prevIndex,
+ ),
+ )
+ lpt_receiver_received_bytes.inc(labelValues = [sender], amount = msg.size.int64)
+ if self.received.hasKeyOrPut(msg.index, currentArrived):
+ inc(self.duplicateCount)
+ self.helper.duplicates.mgetOrPut(msg.index, (msgHash, 0, msg.size)).dupCount.inc()
+ warn "Duplicate message",
+ index = msg.index,
+ hash = msgHash,
+ times_duplicated = self.helper.duplicates[msg.index].dupCount
+ lpt_receiver_duplicate_messages_count.inc(labelValues = [sender])
+ lpt_receiver_distinct_duplicate_messages_count.set(
+ labelValues = [sender], value = self.helper.duplicates.len()
+ )
+ return
+
+ ## detect misorder arrival and possible lost messages
+ if self.helper.prevIndex + 1 < msg.index:
+ inc(self.misorderCount)
+ warn "Misordered message arrival",
+ index = msg.index, expected = self.helper.prevIndex + 1
+ elif self.helper.prevIndex > msg.index:
+ inc(self.lateCount)
+ warn "Late message arrival", index = msg.index, expected = self.helper.prevIndex + 1
+
+ self.helper.maxIndex = max(self.helper.maxIndex, msg.index)
+ self.helper.prevIndex = msg.index
+ self.helper.prevArrivedAt = currentArrived.info.arrivedAt
+ inc(self.receivedMessages)
+ lpt_receiver_received_messages_count.inc(labelValues = [sender])
+ lpt_receiver_missing_messages_count.set(
+ labelValues = [sender], value = (self.helper.maxIndex - self.receivedMessages).int64
+ )
+
+proc addMessage*(
+ self: var PerPeerStatistics,
+ peerId: string,
+ msg: ProtocolTesterMessage,
+ msgHash: string,
+) =
+ if not self.contains(peerId):
+ self[peerId] = Statistics.init()
+
+ let shortSenderId = block:
+ let senderPeer = PeerId.init(msg.sender)
+ if senderPeer.isErr():
+ msg.sender
+ else:
+ senderPeer.get().shortLog()
+
+ discard catch:
+ self[peerId].addMessage(shortSenderId, msg, msgHash)
+
+ lpt_receiver_sender_peer_count.set(value = self.len)
+
+proc lastMessageArrivedAt*(self: Statistics): Option[Moment] =
+ if self.receivedMessages > 0:
+ return some(self.helper.prevArrivedAt)
+ return none(Moment)
+
+proc lossCount*(self: Statistics): uint32 =
+ self.helper.maxIndex - self.receivedMessages
+
+proc calcLatency*(self: Statistics): tuple[min, max, avg: Duration] =
+ var
+ minLatency = nanos(0)
+ maxLatency = nanos(0)
+ avgLatency = nanos(0)
+
+ if self.receivedMessages > 2:
+ try:
+ var prevArrivedAt = self.received[self.firstReceivedIdx].info.arrivedAt
+
+ for idx, (msg, arrival) in self.received.pairs:
+ if idx <= 1:
+ continue
+ let expectedDelay = nanos(msg.sincePrev)
+
+ ## latency will be 0 if arrived in shorter time than expected
+ var latency = arrival.arrivedAt - arrival.prevArrivedAt - expectedDelay
+
+ ## will not measure zero latency, it is unlikely to happen but in case happens could
+ ## ditort the min latency calulculation as we want to calculate the feasible minimum.
+ if latency > nanos(0):
+ if minLatency == nanos(0):
+ minLatency = latency
+ else:
+ minLatency = min(minLatency, latency)
+
+ maxLatency = max(maxLatency, latency)
+ avgLatency += latency
+
+ avgLatency = avgLatency div (self.receivedMessages - 1)
+ except KeyError:
+ error "Error while calculating latency: " & getCurrentExceptionMsg()
+
+ return (minLatency, maxLatency, avgLatency)
+
+proc missingIndices*(self: Statistics): seq[uint32] =
+ var missing: seq[uint32] = @[]
+ for idx in 1 .. self.helper.maxIndex:
+ if not self.received.hasKey(idx):
+ missing.add(idx)
+ return missing
+
+proc distinctDupCount(self: Statistics): int {.inline.} =
+ return self.helper.duplicates.len()
+
+proc allDuplicates(self: Statistics): int {.inline.} =
+ var total = 0
+ for _, (_, dupCount, _) in self.helper.duplicates.pairs:
+ total += dupCount
+ return total
+
+proc dupMsgs(self: Statistics): string =
+ var dupMsgs: string = ""
+ for idx, (hash, dupCount, size) in self.helper.duplicates.pairs:
+ dupMsgs.add(
+ " index: " & $idx & " | hash: " & hash & " | count: " & $dupCount & " | size: " &
+ $size & "\n"
+ )
+ return dupMsgs
+
+proc echoStat*(self: Statistics, peerId: string) =
+ let (minL, maxL, avgL) = self.calcLatency()
+ lpt_receiver_latencies.set(labelValues = [peerId, "min"], value = minL.nanos())
+ lpt_receiver_latencies.set(labelValues = [peerId, "avg"], value = avgL.nanos())
+ lpt_receiver_latencies.set(labelValues = [peerId, "max"], value = maxL.nanos())
+
+ let printable = catch:
+ """*------------------------------------------------------------------------------------------*
+| Expected | Received | Target | Loss | Misorder | Late | |
+|{self.helper.maxIndex:>11} |{self.receivedMessages:>11} |{self.allMessageCount:>11} |{self.lossCount():>11} |{self.misorderCount:>11} |{self.lateCount:>11} | |
+*------------------------------------------------------------------------------------------*
+| Latency stat: |
+| min latency: {$minL:<73}|
+| avg latency: {$avgL:<73}|
+| max latency: {$maxL:<73}|
+*------------------------------------------------------------------------------------------*
+| Duplicate stat: |
+| distinct duplicate messages: {$self.distinctDupCount():<57}|
+| sum duplicates : {$self.allDuplicates():<57}|
+ Duplicated messages:
+ {self.dupMsgs()}
+*------------------------------------------------------------------------------------------*
+| Lost indices: |
+| {self.missingIndices()} |
+*------------------------------------------------------------------------------------------*""".fmt()
+
+ if printable.isErr():
+ echo "Error while printing statistics: " & printable.error().msg
+ else:
+ echo printable.get()
+
+proc jsonStat*(self: Statistics): string =
+ let minL, maxL, avgL = self.calcLatency()
+
+ let json = catch:
+ """{{"expected":{self.helper.maxIndex},
+ "received": {self.receivedMessages},
+ "target": {self.allMessageCount},
+ "loss": {self.lossCount()},
+ "misorder": {self.misorderCount},
+ "late": {self.lateCount},
+ "duplicate": {self.duplicateCount},
+ "latency":
+ {{"avg": "{avgL}",
+ "min": "{minL}",
+ "max": "{maxL}"
+ }},
+ "lostIndices": {self.missingIndices()}
+ }}""".fmt()
+ if json.isErr:
+ return "{\"result:\": \"" & json.error.msg & "\"}"
+
+ return json.get()
+
+proc echoStats*(self: var PerPeerStatistics) =
+ for peerId, stats in self.pairs:
+ let peerLine = catch:
+ "Receiver statistics from peer {peerId}".fmt()
+ if peerLine.isErr:
+ echo "Error while printing statistics"
+ else:
+ echo peerLine.get()
+ stats.echoStat(peerId)
+
+proc jsonStats*(self: PerPeerStatistics): string =
+ try:
+ #!fmt: off
+ var json = "{\"statistics\": ["
+ var first = true
+ for peerId, stats in self.pairs:
+ if first:
+ first = false
+ else:
+ json.add(", ")
+ json.add("{{\"sender\": \"{peerId}\", \"stat\":".fmt())
+ json.add(stats.jsonStat())
+ json.add("}")
+ json.add("]}")
+ return json
+ #!fmt: on
+ except CatchableError:
+ return
+ "{\"result:\": \"Error while generating json stats: " & getCurrentExceptionMsg() &
+ "\"}"
+
+proc lastMessageArrivedAt*(self: PerPeerStatistics): Option[Moment] =
+ var lastArrivedAt = Moment.init(0, Millisecond)
+ for stat in self.values:
+ let lastMsgFromPeerAt = stat.lastMessageArrivedAt().valueOr:
+ continue
+
+ if lastMsgFromPeerAt > lastArrivedAt:
+ lastArrivedAt = lastMsgFromPeerAt
+
+ if lastArrivedAt == Moment.init(0, Millisecond):
+ return none(Moment)
+
+ return some(lastArrivedAt)
+
+proc checkIfAllMessagesReceived*(
+ self: PerPeerStatistics, maxWaitForLastMessage: Duration
+): Future[bool] {.async.} =
+ # if there are no peers have sent messages, assume we just have started.
+ if self.len == 0:
+ return false
+
+ # check if numerically all messages are received.
+ # this suggest we received at least one message already from one peer
+ var isAlllMessageReceived = true
+ for stat in self.values:
+ if (stat.allMessageCount == 0 and stat.receivedMessages == 0) or
+ stat.helper.maxIndex < stat.allMessageCount:
+ isAlllMessageReceived = false
+ break
+
+ if not isAlllMessageReceived:
+ # if not all message received we still need to check if last message arrived within a time frame
+ # to avoid endless waiting while publishers are already quit.
+ let lastMessageAt = self.lastMessageArrivedAt()
+ if lastMessageAt.isNone():
+ return false
+
+ # last message shall arrived within time limit
+ if Moment.now() - lastMessageAt.get() < maxWaitForLastMessage:
+ return false
+ else:
+ info "No message since max wait time", maxWait = $maxWaitForLastMessage
+
+ ## Ok, we see last message arrived from all peers,
+ ## lets check if all messages are received
+ ## and if not let's wait another 20 secs to give chance the system will send them.
+ var shallWait = false
+ for stat in self.values:
+ if stat.receivedMessages < stat.allMessageCount:
+ shallWait = true
+
+ if shallWait:
+ await sleepAsync(20.seconds)
+
+ return true
diff --git a/third-party/nwaku/apps/liteprotocoltester/tester_config.nim b/third-party/nwaku/apps/liteprotocoltester/tester_config.nim
new file mode 100644
index 0000000..dee918b
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/tester_config.nim
@@ -0,0 +1,208 @@
+import
+ results,
+ chronos,
+ confutils,
+ confutils/defs,
+ confutils/std/net,
+ confutils/toml/defs as confTomlDefs,
+ confutils/toml/std/net as confTomlNet,
+ libp2p/crypto/crypto,
+ libp2p/crypto/secp,
+ libp2p/multiaddress,
+ secp256k1
+
+import
+ ../../tools/confutils/
+ [cli_args, envvar as confEnvvarDefs, envvar_net as confEnvvarNet],
+ waku/[common/logging, waku_core, waku_core/topics/pubsub_topic]
+
+export confTomlDefs, confTomlNet, confEnvvarDefs, confEnvvarNet
+
+const
+ LitePubsubTopic* = PubsubTopic("/waku/2/rs/66/0")
+ LiteContentTopic* = ContentTopic("/tester/1/light-pubsub-example/proto")
+ DefaultMinTestMessageSizeStr* = "1KiB"
+ DefaultMaxTestMessageSizeStr* = "150KiB"
+
+type TesterFunctionality* = enum
+ SENDER # pumps messages to the network
+ RECEIVER # gather and analyze messages from the network
+
+type LightpushVersion* = enum
+ LEGACY # legacy lightpush protocol
+ V3 # lightpush v3 protocol
+
+type LiteProtocolTesterConf* = object
+ configFile* {.
+ desc:
+ "Loads configuration from a TOML file (cmd-line parameters take precedence) for the light waku node",
+ name: "config-file"
+ .}: Option[InputFile]
+
+ ## Log configuration
+ logLevel* {.
+ desc:
+ "Sets the log level for process. Supported levels: TRACE, DEBUG, INFO, NOTICE, WARN, ERROR or FATAL",
+ defaultValue: logging.LogLevel.DEBUG,
+ name: "log-level"
+ .}: logging.LogLevel
+
+ logFormat* {.
+ desc:
+ "Specifies what kind of logs should be written to stdout. Supported formats: TEXT, JSON",
+ defaultValue: logging.LogFormat.TEXT,
+ name: "log-format"
+ .}: logging.LogFormat
+
+ ## Test configuration
+ serviceNode* {.
+ desc: "Peer multiaddr of the service node.", defaultValue: "", name: "service-node"
+ .}: string
+
+ bootstrapNode* {.
+ desc:
+ "Peer multiaddr of the bootstrap node. If `service-node` not set, it is used to retrieve potential service nodes of the network.",
+ defaultValue: "",
+ name: "bootstrap-node"
+ .}: string
+
+ nat* {.
+ desc:
+ "Specify method to use for determining public address. " &
+ "Must be one of: any, none, upnp, pmp, extip:.",
+ defaultValue: "any"
+ .}: string
+
+ testFunc* {.
+ desc: "Specifies the lite protocol tester side. Supported values: sender, receiver.",
+ defaultValue: TesterFunctionality.RECEIVER,
+ name: "test-func"
+ .}: TesterFunctionality
+
+ lightpushVersion* {.
+ desc: "Version of the sender to use. Supported values: legacy, v3.",
+ defaultValue: LightpushVersion.LEGACY,
+ name: "lightpush-version"
+ .}: LightpushVersion
+
+ numMessages* {.
+ desc: "Number of messages to send.", defaultValue: 120, name: "num-messages"
+ .}: uint32
+
+ startPublishingAfter* {.
+ desc: "Wait number of seconds before start publishing messages.",
+ defaultValue: 5,
+ name: "start-publishing-after"
+ .}: uint32
+
+ messageInterval* {.
+ desc: "Delay between messages in milliseconds.",
+ defaultValue: 1000,
+ name: "message-interval"
+ .}: uint32
+
+ shard* {.desc: "Shards index to subscribe to. ", defaultValue: 0, name: "shard".}:
+ uint16
+
+ contentTopics* {.
+ desc: "Default content topic to subscribe to. Argument may be repeated.",
+ defaultValue: @[LiteContentTopic],
+ name: "content-topic"
+ .}: seq[ContentTopic]
+
+ clusterId* {.
+ desc:
+ "Cluster id that the node is running in. Node in a different cluster id is disconnected.",
+ defaultValue: 0,
+ name: "cluster-id"
+ .}: uint16
+
+ minTestMessageSize* {.
+ desc:
+ "Minimum message size. Accepted units: KiB, KB, and B. e.g. 1024KiB; 1500 B; etc.",
+ defaultValue: DefaultMinTestMessageSizeStr,
+ name: "min-test-msg-size"
+ .}: string
+
+ maxTestMessageSize* {.
+ desc:
+ "Maximum message size. Accepted units: KiB, KB, and B. e.g. 1024KiB; 1500 B; etc.",
+ defaultValue: DefaultMaxTestMessageSizeStr,
+ name: "max-test-msg-size"
+ .}: string
+ ## Tester REST service configuration
+ restAddress* {.
+ desc: "Listening address of the REST HTTP server.",
+ defaultValue: parseIpAddress("127.0.0.1"),
+ name: "rest-address"
+ .}: IpAddress
+
+ testPeers* {.
+ desc: "Run dial test on gathered PeerExchange peers.",
+ defaultValue: false,
+ name: "test-peers"
+ .}: bool
+
+ reqPxPeers* {.
+ desc: "Number of peers to request on PeerExchange.",
+ defaultValue: 100,
+ name: "req-px-peers"
+ .}: uint16
+
+ restPort* {.
+ desc: "Listening port of the REST HTTP server.",
+ defaultValue: 8654,
+ name: "rest-port"
+ .}: uint16
+
+ fixedServicePeer* {.
+ desc:
+ "Prevent changing the service peer in case of failures, the full test will stict to the first service peer in use.",
+ defaultValue: false,
+ name: "fixed-service-peer"
+ .}: bool
+
+ restAllowOrigin* {.
+ desc:
+ "Allow cross-origin requests from the specified origin." &
+ "Argument may be repeated." & "Wildcards: * or ? allowed." &
+ "Ex.: \"localhost:*\" or \"127.0.0.1:8080\"",
+ defaultValue: @["*"],
+ name: "rest-allow-origin"
+ .}: seq[string]
+
+ metricsPort* {.
+ desc: "Listening port of the Metrics HTTP server.",
+ defaultValue: 8003,
+ name: "metrics-port"
+ .}: uint16
+
+{.push warning[ProveInit]: off.}
+
+proc load*(T: type LiteProtocolTesterConf, version = ""): ConfResult[T] =
+ try:
+ let conf = LiteProtocolTesterConf.load(
+ version = version,
+ secondarySources = proc(
+ conf: LiteProtocolTesterConf, sources: auto
+ ) {.gcsafe, raises: [ConfigurationError].} =
+ sources.addConfigFile(Envvar, InputFile("liteprotocoltester")),
+ )
+ ok(conf)
+ except CatchableError:
+ err(getCurrentExceptionMsg())
+
+proc getPubsubTopic*(conf: LiteProtocolTesterConf): PubsubTopic =
+ return $RelayShard(clusterId: conf.clusterId, shardId: conf.shard)
+
+proc getCodec*(conf: LiteProtocolTesterConf): string =
+ return
+ if conf.testFunc == TesterFunctionality.RECEIVER:
+ WakuFilterSubscribeCodec
+ else:
+ if conf.lightpushVersion == LightpushVersion.LEGACY:
+ WakuLegacyLightPushCodec
+ else:
+ WakuLightPushCodec
+
+{.pop.}
diff --git a/third-party/nwaku/apps/liteprotocoltester/tester_message.nim b/third-party/nwaku/apps/liteprotocoltester/tester_message.nim
new file mode 100644
index 0000000..eeff7b5
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/tester_message.nim
@@ -0,0 +1,121 @@
+{.push raises: [].}
+
+import
+ chronicles,
+ json_serialization,
+ json_serialization/std/options,
+ json_serialization/lexer
+
+import ../../waku/waku_api/rest/serdes
+
+type ProtocolTesterMessage* = object
+ sender*: string
+ index*: uint32
+ count*: uint32
+ startedAt*: int64
+ sinceStart*: int64
+ sincePrev*: int64
+ size*: uint64
+
+proc writeValue*(
+ writer: var JsonWriter[RestJson], value: ProtocolTesterMessage
+) {.raises: [IOError].} =
+ writer.beginRecord()
+ writer.writeField("sender", value.sender)
+ writer.writeField("index", value.index)
+ writer.writeField("count", value.count)
+ writer.writeField("startedAt", value.startedAt)
+ writer.writeField("sinceStart", value.sinceStart)
+ writer.writeField("sincePrev", value.sincePrev)
+ writer.writeField("size", value.size)
+ writer.endRecord()
+
+proc readValue*(
+ reader: var JsonReader[RestJson], value: var ProtocolTesterMessage
+) {.gcsafe, raises: [SerializationError, IOError].} =
+ var
+ sender: Option[string]
+ index: Option[uint32]
+ count: Option[uint32]
+ startedAt: Option[int64]
+ sinceStart: Option[int64]
+ sincePrev: Option[int64]
+ size: Option[uint64]
+
+ for fieldName in readObjectFields(reader):
+ case fieldName
+ of "sender":
+ if sender.isSome():
+ reader.raiseUnexpectedField(
+ "Multiple `sender` fields found", "ProtocolTesterMessage"
+ )
+ sender = some(reader.readValue(string))
+ of "index":
+ if index.isSome():
+ reader.raiseUnexpectedField(
+ "Multiple `index` fields found", "ProtocolTesterMessage"
+ )
+ index = some(reader.readValue(uint32))
+ of "count":
+ if count.isSome():
+ reader.raiseUnexpectedField(
+ "Multiple `count` fields found", "ProtocolTesterMessage"
+ )
+ count = some(reader.readValue(uint32))
+ of "startedAt":
+ if startedAt.isSome():
+ reader.raiseUnexpectedField(
+ "Multiple `startedAt` fields found", "ProtocolTesterMessage"
+ )
+ startedAt = some(reader.readValue(int64))
+ of "sinceStart":
+ if sinceStart.isSome():
+ reader.raiseUnexpectedField(
+ "Multiple `sinceStart` fields found", "ProtocolTesterMessage"
+ )
+ sinceStart = some(reader.readValue(int64))
+ of "sincePrev":
+ if sincePrev.isSome():
+ reader.raiseUnexpectedField(
+ "Multiple `sincePrev` fields found", "ProtocolTesterMessage"
+ )
+ sincePrev = some(reader.readValue(int64))
+ of "size":
+ if size.isSome():
+ reader.raiseUnexpectedField(
+ "Multiple `size` fields found", "ProtocolTesterMessage"
+ )
+ size = some(reader.readValue(uint64))
+ else:
+ unrecognizedFieldWarning(value)
+
+ if sender.isNone():
+ reader.raiseUnexpectedValue("Field `sender` is missing")
+
+ if index.isNone():
+ reader.raiseUnexpectedValue("Field `index` is missing")
+
+ if count.isNone():
+ reader.raiseUnexpectedValue("Field `count` is missing")
+
+ if startedAt.isNone():
+ reader.raiseUnexpectedValue("Field `startedAt` is missing")
+
+ if sinceStart.isNone():
+ reader.raiseUnexpectedValue("Field `sinceStart` is missing")
+
+ if sincePrev.isNone():
+ reader.raiseUnexpectedValue("Field `sincePrev` is missing")
+
+ if size.isNone():
+ reader.raiseUnexpectedValue("Field `size` is missing")
+
+ value = ProtocolTesterMessage(
+ sender: sender.get(),
+ index: index.get(),
+ count: count.get(),
+ startedAt: startedAt.get(),
+ sinceStart: sinceStart.get(),
+ sincePrev: sincePrev.get(),
+ size: size.get(),
+ )
diff --git a/third-party/nwaku/apps/liteprotocoltester/v3_publisher.nim b/third-party/nwaku/apps/liteprotocoltester/v3_publisher.nim
new file mode 100644
index 0000000..c8353b5
--- /dev/null
+++ b/third-party/nwaku/apps/liteprotocoltester/v3_publisher.nim
@@ -0,0 +1,29 @@
+import results, options, chronos
+import waku/[waku_node, waku_core, waku_lightpush, waku_lightpush/common]
+import publisher_base
+
+type V3Publisher* = ref object of PublisherBase
+
+proc new*(T: type V3Publisher, wakuNode: WakuNode): T =
+ if isNil(wakuNode.wakuLightpushClient):
+ wakuNode.mountLightPushClient()
+
+ return V3Publisher(wakuNode: wakuNode)
+
+method send*(
+ self: V3Publisher,
+ topic: PubsubTopic,
+ message: WakuMessage,
+ servicePeer: RemotePeerInfo,
+): Future[Result[void, string]] {.async.} =
+ # when error it must return original error desc due the text is used for distinction between error types in metrics.
+ discard (
+ await self.wakuNode.lightpushPublish(some(topic), message, some(servicePeer))
+ ).valueOr:
+ if error.code == LightPushErrorCode.NO_PEERS_TO_RELAY and
+ error.desc != some("No peers for topic, skipping publish"):
+ # TODO: We need better separation of errors happening on the client side or the server side.-
+ return err("dial_failure")
+ else:
+ return err($error.code)
+ return ok()
diff --git a/third-party/nwaku/apps/networkmonitor/README.md b/third-party/nwaku/apps/networkmonitor/README.md
new file mode 100644
index 0000000..3e10367
--- /dev/null
+++ b/third-party/nwaku/apps/networkmonitor/README.md
@@ -0,0 +1,84 @@
+# networkmonitor
+
+Monitoring tool to run in an existing `waku` network with the following features:
+
+* Keeps discovering new peers using `discv5`
+* Tracks advertised capabilities of each node as per stored in the ENR `waku` field
+* Attempts to connect to all nodes, tracking which protocols each node supports
+* Presents grafana-ready metrics showing the state of the network in terms of locations, ips, number discovered peers, number of peers we could connect to, user-agent that each peer contains, content topics and the amount of rx messages in each one.
+* Metrics are exposed through prometheus metrics but also with a custom rest api, presenting detailed information about each peer. These metrics are exposed via a rest api.
+
+## Usage
+
+```console
+./build/networkmonitor --help
+Usage:
+
+networkmonitor [OPTIONS]...
+
+The following options are available:
+
+ -l, --log-level Sets the log level [=LogLevel.INFO].
+ -t, --timeout Timeout to consider that the connection failed [=chronos.seconds(10)].
+ -b, --bootstrap-node Bootstrap ENR node. Argument may be repeated. [=@[""]].
+ --dns-discovery-url URL for DNS node list in format 'enrtree://@'.
+ --pubsub-topic Default pubsub topic to subscribe to. Argument may be repeated..
+ -r, --refresh-interval How often new peers are discovered and connected to (in seconds) [=5].
+ --cluster-id Cluster id that the node is running in. Node in a different cluster id is
+ disconnected. [=1].
+ --rln-relay Enable spam protection through rln-relay: true|false [=true].
+ --rln-relay-dynamic Enable waku-rln-relay with on-chain dynamic group management: true|false
+ [=true].
+ --rln-relay-eth-client-address HTTP address of an Ethereum testnet client e.g., http://localhost:8540/
+ [=http://localhost:8540/].
+ --rln-relay-eth-contract-address Address of membership contract on an Ethereum testnet.
+ --rln-relay-epoch-sec Epoch size in seconds used to rate limit RLN memberships. Default is 1 second.
+ [=1].
+ --rln-relay-user-message-limit Set a user message limit for the rln membership registration. Must be a positive
+ integer. Default is 1. [=1].
+ --metrics-server Enable the metrics server: true|false [=true].
+ --metrics-server-address Listening address of the metrics server. [=parseIpAddress("127.0.0.1")].
+ --metrics-server-port Listening HTTP port of the metrics server. [=8008].
+ --metrics-rest-address Listening address of the metrics rest server. [=127.0.0.1].
+ --metrics-rest-port Listening HTTP port of the metrics rest server. [=8009].
+```
+
+## Example
+
+Connect to the network through a given bootstrap node, with default parameters. See metrics section for the data that it exposes.
+
+```console
+./build/networkmonitor --log-level=INFO --b="enr:-QEkuEB3WHNS-xA3RDpfu9A2Qycr3bN3u7VoArMEiDIFZJ66F1EB3d4wxZN1hcdcOX-RfuXB-MQauhJGQbpz3qUofOtLAYJpZIJ2NIJpcIQI2SVcim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmFjLWNuLWhvbmdrb25nLWMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwA2Ni9ub2RlLTAxLmFjLWNuLWhvbmdrb25nLWMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQPK35Nnz0cWUtSAhBp7zvHEhyU_AqeQUlqzLiLxfP2L4oN0Y3CCdl-DdWRwgiMohXdha3UyDw"
+```
+
+```console
+./build/networkmonitor --log-level=INFO --dns-discovery-url=enrtree://AL65EKLJAUXKKPG43HVTML5EFFWEZ7L4LOKTLZCLJASG4DSESQZEC@prod.status.nodes.status.im
+```
+
+## Metrics
+
+Metrics are divided into two categories:
+
+* Prometheus metrics, exposed as i.e. gauges.
+* Custom metrics, used for unconstrained labels such as peer information or content topics.
+ - These metrics are not exposed through prometheus because since they are unconstrained, they can end up breaking the backend, as a new datapoint is generated for each one and it can reach up a point where is too much to handle.
+
+### Prometheus Metrics
+
+The following metrics are available. See `http://localhost:8008/metrics`
+
+* `peer_type_as_per_enr`: Number of peers supporting each capability according to the ENR (Relay, Store, Lightpush, Filter)
+* `peer_type_as_per_protocol`: Number of peers supporting each protocol, after a successful connection)
+* `peer_user_agents`: List of useragents found in the network and their count
+
+Other relevant metrics reused from `nim-eth`:
+
+* `routing_table_nodes`: Inherited from nim-eth, number of nodes in the routing table
+* `discovery_message_requests_outgoing_total`: Inherited from nim-eth, number of outgoing discovery requests, useful to know if the node is actively looking for new peers
+
+### Custom Metrics
+
+The following endpoints are available:
+
+* `http://localhost:8009/allpeersinfo`: json list of all peers with extra information such as ip, location, supported protocols and last connection time.
+* `http://localhost:8009/contenttopics`: content topic messages and its message count.
diff --git a/third-party/nwaku/apps/networkmonitor/docker-compose.yml b/third-party/nwaku/apps/networkmonitor/docker-compose.yml
new file mode 100644
index 0000000..d7bf661
--- /dev/null
+++ b/third-party/nwaku/apps/networkmonitor/docker-compose.yml
@@ -0,0 +1,34 @@
+version: '3.8'
+networks:
+ monitoring:
+ driver: bridge
+
+volumes:
+ prometheus-data:
+ driver: local
+ grafana-data:
+ driver: local
+
+# Services definitions
+services:
+
+ prometheus:
+ image: docker.io/prom/prometheus:latest
+ container_name: prometheus
+ ports:
+ - 9090:9090
+ command:
+ - '--config.file=/etc/prometheus/prometheus.yaml'
+ volumes:
+ - ./prometheus.yaml:/etc/prometheus/prometheus.yaml:ro
+ - ./data:/prometheus
+ restart: unless-stopped
+
+ grafana:
+ image: grafana/grafana-oss:latest
+ container_name: grafana
+ ports:
+ - '3000:3000'
+ volumes:
+ - grafana-data:/var/lib/grafana
+ restart: unless-stopped
diff --git a/third-party/nwaku/apps/networkmonitor/networkmonitor.nim b/third-party/nwaku/apps/networkmonitor/networkmonitor.nim
new file mode 100644
index 0000000..a9144ae
--- /dev/null
+++ b/third-party/nwaku/apps/networkmonitor/networkmonitor.nim
@@ -0,0 +1,668 @@
+{.push raises: [].}
+
+import
+ std/[net, tables, strutils, times, sequtils, random],
+ results,
+ chronicles,
+ chronicles/topics_registry,
+ chronos,
+ chronos/timer as ctime,
+ confutils,
+ eth/keys,
+ eth/p2p/discoveryv5/enr,
+ libp2p/crypto/crypto,
+ libp2p/nameresolving/dnsresolver,
+ libp2p/protocols/ping,
+ metrics,
+ metrics/chronos_httpserver,
+ presto/[route, server, client]
+import
+ waku/[
+ waku_core,
+ node/peer_manager,
+ waku_node,
+ waku_enr,
+ discovery/waku_discv5,
+ discovery/waku_dnsdisc,
+ waku_relay,
+ waku_rln_relay,
+ factory/builder,
+ factory/networks_config,
+ ],
+ ./networkmonitor_metrics,
+ ./networkmonitor_config,
+ ./networkmonitor_utils
+
+logScope:
+ topics = "networkmonitor"
+
+const ReconnectTime = 60
+const MaxConnectionRetries = 5
+const ResetRetriesAfter = 1200
+const PingSmoothing = 0.3
+const MaxConnectedPeers = 150
+
+const git_version* {.strdefine.} = "n/a"
+
+proc setDiscoveredPeersCapabilities(routingTableNodes: seq[waku_enr.Record]) =
+ for capability in @[Relay, Store, Filter, Lightpush]:
+ let nOfNodesWithCapability =
+ routingTableNodes.countIt(it.supportsCapability(capability))
+ info "capabilities as per ENR waku flag",
+ capability = capability, amount = nOfNodesWithCapability
+ networkmonitor_peer_type_as_per_enr.set(
+ int64(nOfNodesWithCapability), labelValues = [$capability]
+ )
+
+proc setDiscoveredPeersCluster(routingTableNodes: seq[Node]) =
+ var clusters: CountTable[uint16]
+
+ for node in routingTableNodes:
+ let typedRec = node.record.toTyped().valueOr:
+ clusters.inc(0)
+ continue
+
+ let relayShard = typedRec.relaySharding().valueOr:
+ clusters.inc(0)
+ continue
+
+ clusters.inc(relayShard.clusterId)
+
+ for (key, value) in clusters.pairs:
+ networkmonitor_peer_cluster_as_per_enr.set(int64(value), labelValues = [$key])
+
+proc analyzePeer(
+ customPeerInfo: CustomPeerInfoRef,
+ peerInfo: RemotePeerInfo,
+ node: WakuNode,
+ timeout: chronos.Duration,
+): Future[Result[string, string]] {.async.} =
+ var pingDelay: chronos.Duration
+
+ proc ping(): Future[Result[void, string]] {.async, gcsafe.} =
+ try:
+ let conn = await node.switch.dial(peerInfo.peerId, peerInfo.addrs, PingCodec)
+ pingDelay = await node.libp2pPing.ping(conn)
+ return ok()
+ except CatchableError:
+ var msg = getCurrentExceptionMsg()
+ if msg == "Future operation cancelled!":
+ msg = "timedout"
+ warn "failed to ping the peer", peer = peerInfo, err = msg
+
+ customPeerInfo.connError = msg
+ return err("could not ping peer: " & msg)
+
+ let timedOut = not await ping().withTimeout(timeout)
+ # need this check for pingDelat == 0 because there may be a conn error before timeout
+ if timedOut or pingDelay == 0.millis:
+ customPeerInfo.retries += 1
+ return err(customPeerInfo.connError)
+
+ customPeerInfo.connError = ""
+ info "successfully pinged peer", peer = peerInfo, duration = pingDelay.millis
+ networkmonitor_peer_ping.observe(pingDelay.millis)
+
+ # We are using a smoothed moving average
+ customPeerInfo.avgPingDuration =
+ if customPeerInfo.avgPingDuration.millis == 0:
+ pingDelay
+ else:
+ let newAvg =
+ (float64(pingDelay.millis) * PingSmoothing) +
+ float64(customPeerInfo.avgPingDuration.millis) * (1.0 - PingSmoothing)
+
+ int64(newAvg).millis
+
+ customPeerInfo.lastPingDuration = pingDelay
+
+ return ok(customPeerInfo.peerId)
+
+proc shouldReconnect(customPeerInfo: CustomPeerInfoRef): bool =
+ let reconnetIntervalCheck =
+ getTime().toUnix() >= customPeerInfo.lastTimeConnected + ReconnectTime
+ var retriesCheck = customPeerInfo.retries < MaxConnectionRetries
+
+ if not retriesCheck and
+ getTime().toUnix() >= customPeerInfo.lastTimeConnected + ResetRetriesAfter:
+ customPeerInfo.retries = 0
+ retriesCheck = true
+ info "resetting retries counter", peerId = customPeerInfo.peerId
+
+ return reconnetIntervalCheck and retriesCheck
+
+# TODO: Split in discover, connect
+proc setConnectedPeersMetrics(
+ discoveredNodes: seq[waku_enr.Record],
+ node: WakuNode,
+ timeout: chronos.Duration,
+ restClient: RestClientRef,
+ allPeers: CustomPeersTableRef,
+) {.async.} =
+ let currentTime = getTime().toUnix()
+
+ var newPeers = 0
+ var successfulConnections = 0
+
+ var analyzeFuts: seq[Future[Result[string, string]]]
+
+ var (inConns, outConns) = node.peer_manager.connectedPeers(WakuRelayCodec)
+ info "connected peers", inConns = inConns.len, outConns = outConns.len
+
+ shuffle(outConns)
+
+ if outConns.len >= toInt(MaxConnectedPeers / 2):
+ for p in outConns[0 ..< toInt(outConns.len / 2)]:
+ trace "Pruning Peer", Peer = $p
+ asyncSpawn(node.switch.disconnect(p))
+
+ # iterate all newly discovered nodes
+ for discNode in discoveredNodes:
+ let peerRes = toRemotePeerInfo(discNode)
+
+ let peerInfo = peerRes.valueOr:
+ warn "error converting record to remote peer info", record = discNode
+ continue
+
+ # create new entry if new peerId found
+ let peerId = $peerInfo.peerId
+
+ if not allPeers.hasKey(peerId):
+ allPeers[peerId] = CustomPeerInfoRef(peerId: peerId)
+ newPeers += 1
+ else:
+ info "already seen", peerId = peerId
+
+ let customPeerInfo = allPeers[peerId]
+
+ customPeerInfo.lastTimeDiscovered = currentTime
+ customPeerInfo.enr = discNode.toURI()
+ customPeerInfo.enrCapabilities = discNode.getCapabilities().mapIt($it)
+ customPeerInfo.discovered += 1
+
+ for maddr in peerInfo.addrs:
+ if $maddr notin customPeerInfo.maddrs:
+ customPeerInfo.maddrs.add $maddr
+ let typedRecord = discNode.toTypedRecord()
+ if not typedRecord.isOk():
+ warn "could not convert record to typed record", record = discNode
+ continue
+ if not typedRecord.get().ip.isSome():
+ warn "ip field is not set", record = typedRecord.get()
+ continue
+
+ let ip = $typedRecord.get().ip.get().join(".")
+ customPeerInfo.ip = ip
+
+ # try to ping the peer
+ if shouldReconnect(customPeerInfo):
+ if customPeerInfo.retries > 0:
+ warn "trying to dial failed peer again",
+ peerId = peerId, retry = customPeerInfo.retries
+ analyzeFuts.add(analyzePeer(customPeerInfo, peerInfo, node, timeout))
+
+ # Wait for all connection attempts to finish
+ let analyzedPeers = await allFinished(analyzeFuts)
+
+ for peerIdFut in analyzedPeers:
+ let peerIdRes = await peerIdFut
+ let peerIdStr = peerIdRes.valueOr:
+ continue
+
+ successfulConnections += 1
+ let peerId = PeerId.init(peerIdStr).valueOr:
+ warn "failed to parse peerId", peerId = peerIdStr
+ continue
+ var customPeerInfo = allPeers[peerIdStr]
+
+ debug "connected to peer", peer = customPeerInfo[]
+
+ # after connection, get supported protocols
+ let lp2pPeerStore = node.switch.peerStore
+ let nodeProtocols = lp2pPeerStore[ProtoBook][peerId]
+ customPeerInfo.supportedProtocols = nodeProtocols
+ customPeerInfo.lastTimeConnected = currentTime
+
+ # after connection, get user-agent
+ let nodeUserAgent = lp2pPeerStore[AgentBook][peerId]
+ customPeerInfo.userAgent = nodeUserAgent
+
+ info "number of newly discovered peers", amount = newPeers
+ # inform the total connections that we did in this round
+ info "number of successful connections", amount = successfulConnections
+
+proc updateMetrics(allPeersRef: CustomPeersTableRef) {.gcsafe.} =
+ var allProtocols: Table[string, int]
+ var allAgentStrings: Table[string, int]
+ var countries: Table[string, int]
+ var connectedPeers = 0
+ var failedPeers = 0
+
+ for peerInfo in allPeersRef.values:
+ if peerInfo.connError == "":
+ for protocol in peerInfo.supportedProtocols:
+ allProtocols[protocol] = allProtocols.mgetOrPut(protocol, 0) + 1
+
+ # store available user-agents in the network
+ allAgentStrings[peerInfo.userAgent] =
+ allAgentStrings.mgetOrPut(peerInfo.userAgent, 0) + 1
+
+ if peerInfo.country != "":
+ countries[peerInfo.country] = countries.mgetOrPut(peerInfo.country, 0) + 1
+
+ connectedPeers += 1
+ else:
+ failedPeers += 1
+
+ networkmonitor_peer_count.set(int64(connectedPeers), labelValues = ["true"])
+ networkmonitor_peer_count.set(int64(failedPeers), labelValues = ["false"])
+ # update count on each protocol
+ for protocol in allProtocols.keys():
+ let countOfProtocols = allProtocols.mgetOrPut(protocol, 0)
+ networkmonitor_peer_type_as_per_protocol.set(
+ int64(countOfProtocols), labelValues = [protocol]
+ )
+ info "supported protocols in the network",
+ protocol = protocol, count = countOfProtocols
+
+ # update count on each user-agent
+ for userAgent in allAgentStrings.keys():
+ let countOfUserAgent = allAgentStrings.mgetOrPut(userAgent, 0)
+ networkmonitor_peer_user_agents.set(
+ int64(countOfUserAgent), labelValues = [userAgent]
+ )
+ info "user agents participating in the network",
+ userAgent = userAgent, count = countOfUserAgent
+
+ for country in countries.keys():
+ let peerCount = countries.mgetOrPut(country, 0)
+ networkmonitor_peer_country_count.set(int64(peerCount), labelValues = [country])
+ info "number of peers per country", country = country, count = peerCount
+
+proc populateInfoFromIp(
+ allPeersRef: CustomPeersTableRef, restClient: RestClientRef
+) {.async.} =
+ for peer in allPeersRef.keys():
+ if allPeersRef[peer].country != "" and allPeersRef[peer].city != "":
+ continue
+ # TODO: Update also if last update > x
+ if allPeersRef[peer].ip == "":
+ continue
+ # get more info the peers from its ip address
+ var location: NodeLocation
+ try:
+ # IP-API endpoints are now limited to 45 HTTP requests per minute
+ await sleepAsync(1400.millis)
+ let response = await restClient.ipToLocation(allPeersRef[peer].ip)
+ location = response.data
+ except CatchableError:
+ warn "could not get location", ip = allPeersRef[peer].ip
+ continue
+ allPeersRef[peer].country = location.country
+ allPeersRef[peer].city = location.city
+
+# TODO: Split in discovery, connections, and ip2location
+# crawls the network discovering peers and trying to connect to them
+# metrics are processed and exposed
+proc crawlNetwork(
+ node: WakuNode,
+ wakuDiscv5: WakuDiscoveryV5,
+ restClient: RestClientRef,
+ conf: NetworkMonitorConf,
+ allPeersRef: CustomPeersTableRef,
+) {.async.} =
+ let crawlInterval = conf.refreshInterval * 1000
+ while true:
+ let startTime = Moment.now()
+ # discover new random nodes
+ let discoveredNodes = await wakuDiscv5.findRandomPeers()
+
+ # nodes are nested into bucket, flat it
+ let flatNodes = wakuDiscv5.protocol.routingTable.buckets.mapIt(it.nodes).flatten()
+
+ # populate metrics related to capabilities as advertised by the ENR (see waku field)
+ setDiscoveredPeersCapabilities(discoveredNodes)
+
+ # populate cluster metrics as advertised by the ENR
+ setDiscoveredPeersCluster(flatNodes)
+
+ # tries to connect to all newly discovered nodes
+ # and populates metrics related to peers we could connect
+ # note random discovered nodes can be already known
+ await setConnectedPeersMetrics(
+ discoveredNodes, node, conf.timeout, restClient, allPeersRef
+ )
+
+ updateMetrics(allPeersRef)
+
+ # populate info from ip addresses
+ await populateInfoFromIp(allPeersRef, restClient)
+
+ let totalNodes = discoveredNodes.len
+ #let seenNodes = totalNodes
+
+ info "discovered nodes: ", total = totalNodes #, seen = seenNodes
+
+ # Notes:
+ # we dont run ipMajorityLoop
+ # we dont run revalidateLoop
+ let endTime = Moment.now()
+ let elapsed = (endTime - startTime).nanos
+
+ info "crawl duration", time = elapsed.millis
+
+ await sleepAsync(crawlInterval.millis - elapsed.millis)
+
+proc retrieveDynamicBootstrapNodes(
+ dnsDiscoveryUrl: string, dnsAddrsNameServers: seq[IpAddress]
+): Future[Result[seq[RemotePeerInfo], string]] {.async.} =
+ ## Retrieve dynamic bootstrap nodes (DNS discovery)
+
+ if dnsDiscoveryUrl != "":
+ # DNS discovery
+ debug "Discovering nodes using Waku DNS discovery", url = dnsDiscoveryUrl
+
+ var nameServers: seq[TransportAddress]
+ for ip in dnsAddrsNameServers:
+ nameServers.add(initTAddress(ip, Port(53))) # Assume all servers use port 53
+
+ let dnsResolver = DnsResolver.new(nameServers)
+
+ proc resolver(domain: string): Future[string] {.async, gcsafe.} =
+ trace "resolving", domain = domain
+ let resolved = await dnsResolver.resolveTxt(domain)
+ if resolved.len > 0:
+ return resolved[0] # Use only first answer
+
+ var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl, resolver)
+ if wakuDnsDiscovery.isOk():
+ return (await wakuDnsDiscovery.get().findPeers()).mapErr(
+ proc(e: cstring): string =
+ $e
+ )
+ else:
+ warn "Failed to init Waku DNS discovery"
+
+ debug "No method for retrieving dynamic bootstrap nodes specified."
+ ok(newSeq[RemotePeerInfo]()) # Return an empty seq by default
+
+proc getBootstrapFromDiscDns(
+ conf: NetworkMonitorConf
+): Future[Result[seq[enr.Record], string]] {.async.} =
+ try:
+ let dnsNameServers = @[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")]
+ let dynamicBootstrapNodesRes =
+ await retrieveDynamicBootstrapNodes(conf.dnsDiscoveryUrl, dnsNameServers)
+ if not dynamicBootstrapNodesRes.isOk():
+ error("failed discovering peers from DNS")
+ let dynamicBootstrapNodes = dynamicBootstrapNodesRes.get()
+
+ # select dynamic bootstrap nodes that have an ENR containing a udp port.
+ # Discv5 only supports UDP https://github.com/ethereum/devp2p/blob/master/discv5/discv5-theory.md)
+ var discv5BootstrapEnrs: seq[enr.Record]
+ for n in dynamicBootstrapNodes:
+ if n.enr.isSome():
+ let
+ enr = n.enr.get()
+ tenrRes = enr.toTypedRecord()
+ if tenrRes.isOk() and (
+ tenrRes.get().udp.isSome() or tenrRes.get().udp6.isSome()
+ ):
+ discv5BootstrapEnrs.add(enr)
+ return ok(discv5BootstrapEnrs)
+ except CatchableError:
+ error("failed discovering peers from DNS")
+
+proc initAndStartApp(
+ conf: NetworkMonitorConf
+): Future[Result[(WakuNode, WakuDiscoveryV5), string]] {.async.} =
+ let bindIp =
+ try:
+ parseIpAddress("0.0.0.0")
+ except CatchableError:
+ return err("could not start node: " & getCurrentExceptionMsg())
+
+ let extIp =
+ try:
+ parseIpAddress("127.0.0.1")
+ except CatchableError:
+ return err("could not start node: " & getCurrentExceptionMsg())
+
+ let
+ # some hardcoded parameters
+ rng = keys.newRng()
+ key = crypto.PrivateKey.random(Secp256k1, rng[])[]
+ nodeTcpPort = Port(60000)
+ nodeUdpPort = Port(9000)
+ flags = CapabilitiesBitfield.init(
+ lightpush = false, filter = false, store = false, relay = true
+ )
+
+ var builder = EnrBuilder.init(key)
+
+ builder.withIpAddressAndPorts(
+ ipAddr = some(extIp), tcpPort = some(nodeTcpPort), udpPort = some(nodeUdpPort)
+ )
+ builder.withWakuCapabilities(flags)
+
+ builder.withWakuRelaySharding(
+ RelayShards(clusterId: conf.clusterId, shardIds: conf.shards)
+ ).isOkOr:
+ error "failed to add sharded topics to ENR", error = error
+ return err("failed to add sharded topics to ENR: " & $error)
+
+ let recordRes = builder.build()
+ let record =
+ if recordRes.isErr():
+ return err("cannot build record: " & $recordRes.error)
+ else:
+ recordRes.get()
+
+ var nodeBuilder = WakuNodeBuilder.init()
+
+ nodeBuilder.withNodeKey(key)
+ nodeBuilder.withRecord(record)
+ nodeBuilder.withSwitchConfiguration(maxConnections = some(MaxConnectedPeers))
+
+ nodeBuilder.withPeerManagerConfig(
+ maxConnections = MaxConnectedPeers,
+ relayServiceRatio = "13.33:86.67",
+ shardAware = true,
+ )
+ let res = nodeBuilder.withNetworkConfigurationDetails(bindIp, nodeTcpPort)
+ if res.isErr():
+ return err("node building error" & $res.error)
+
+ let nodeRes = nodeBuilder.build()
+ let node =
+ if nodeRes.isErr():
+ return err("node building error" & $res.error)
+ else:
+ nodeRes.get()
+
+ var discv5BootstrapEnrsRes = await getBootstrapFromDiscDns(conf)
+ if discv5BootstrapEnrsRes.isErr():
+ error("failed discovering peers from DNS")
+ var discv5BootstrapEnrs = discv5BootstrapEnrsRes.get()
+
+ # parse enrURIs from the configuration and add the resulting ENRs to the discv5BootstrapEnrs seq
+ for enrUri in conf.bootstrapNodes:
+ addBootstrapNode(enrUri, discv5BootstrapEnrs)
+
+ # discv5
+ let discv5Conf = WakuDiscoveryV5Config(
+ discv5Config: none(DiscoveryConfig),
+ address: bindIp,
+ port: nodeUdpPort,
+ privateKey: keys.PrivateKey(key.skkey),
+ bootstrapRecords: discv5BootstrapEnrs,
+ autoupdateRecord: false,
+ )
+
+ let wakuDiscv5 = WakuDiscoveryV5.new(node.rng, discv5Conf, some(record))
+
+ try:
+ wakuDiscv5.protocol.open()
+ except CatchableError:
+ return err("could not start node: " & getCurrentExceptionMsg())
+
+ ok((node, wakuDiscv5))
+
+proc startRestApiServer(
+ conf: NetworkMonitorConf,
+ allPeersInfo: CustomPeersTableRef,
+ numMessagesPerContentTopic: ContentTopicMessageTableRef,
+): Result[void, string] =
+ try:
+ let serverAddress =
+ initTAddress(conf.metricsRestAddress & ":" & $conf.metricsRestPort)
+ proc validate(pattern: string, value: string): int =
+ if pattern.startsWith("{") and pattern.endsWith("}"): 0 else: 1
+
+ var router = RestRouter.init(validate)
+ router.installHandler(allPeersInfo, numMessagesPerContentTopic)
+ var sres = RestServerRef.new(router, serverAddress)
+ let restServer = sres.get()
+ restServer.start()
+ except CatchableError:
+ error("could not start rest api server")
+ ok()
+
+# handles rx of messages over a topic (see subscribe)
+# counts the number of messages per content topic
+proc subscribeAndHandleMessages(
+ node: WakuNode,
+ pubsubTopic: PubsubTopic,
+ msgPerContentTopic: ContentTopicMessageTableRef,
+) =
+ # handle function
+ proc handler(
+ pubsubTopic: PubsubTopic, msg: WakuMessage
+ ): Future[void] {.async, gcsafe.} =
+ trace "rx message", pubsubTopic = pubsubTopic, contentTopic = msg.contentTopic
+
+ # If we reach a table limit size, remove c topics with the least messages.
+ let tableSize = 100
+ if msgPerContentTopic.len > (tableSize - 1):
+ let minIndex = toSeq(msgPerContentTopic.values()).minIndex()
+ msgPerContentTopic.del(toSeq(msgPerContentTopic.keys())[minIndex])
+
+ # TODO: Will overflow at some point
+ # +1 if content topic existed, init to 1 otherwise
+ if msgPerContentTopic.hasKey(msg.contentTopic):
+ msgPerContentTopic[msg.contentTopic] += 1
+ else:
+ msgPerContentTopic[msg.contentTopic] = 1
+
+ node.subscribe((kind: PubsubSub, topic: pubsubTopic), WakuRelayHandler(handler)).isOkOr:
+ error "failed to subscribe to pubsub topic", pubsubTopic, error
+ quit(1)
+
+when isMainModule:
+ # known issue: confutils.nim(775, 17) Error: can raise an unlisted exception: ref IOError
+ {.pop.}
+ let confRes = NetworkMonitorConf.loadConfig()
+ if confRes.isErr():
+ error "could not load cli variables", err = confRes.error
+ quit(1)
+
+ var conf = confRes.get()
+ info "cli flags", conf = conf
+
+ if conf.clusterId == 1:
+ let twnNetworkConf = NetworkConf.TheWakuNetworkConf()
+
+ conf.bootstrapNodes = twnNetworkConf.discv5BootstrapNodes
+ conf.rlnRelayDynamic = twnNetworkConf.rlnRelayDynamic
+ conf.rlnRelayEthContractAddress = twnNetworkConf.rlnRelayEthContractAddress
+ conf.rlnEpochSizeSec = twnNetworkConf.rlnEpochSizeSec
+ conf.rlnRelayUserMessageLimit = twnNetworkConf.rlnRelayUserMessageLimit
+ conf.numShardsInNetwork = twnNetworkConf.shardingConf.numShardsInCluster
+
+ if conf.shards.len == 0:
+ conf.shards =
+ toSeq(uint16(0) .. uint16(twnNetworkConf.shardingConf.numShardsInCluster - 1))
+
+ if conf.logLevel != LogLevel.NONE:
+ setLogLevel(conf.logLevel)
+
+ # list of peers that we have discovered/connected
+ var allPeersInfo = CustomPeersTableRef()
+
+ # content topic and the number of messages that were received
+ var msgPerContentTopic = ContentTopicMessageTableRef()
+
+ # start metrics server
+ if conf.metricsServer:
+ let res =
+ startMetricsServer(conf.metricsServerAddress, Port(conf.metricsServerPort))
+ if res.isErr():
+ error "could not start metrics server", err = res.error
+ quit(1)
+
+ # start rest server for custom metrics
+ let res = startRestApiServer(conf, allPeersInfo, msgPerContentTopic)
+ if res.isErr():
+ error "could not start rest api server", err = res.error
+ quit(1)
+
+ # create a rest client
+ let clientRest =
+ RestClientRef.new(url = "http://ip-api.com", connectTimeout = ctime.seconds(2))
+ if clientRest.isErr():
+ error "could not start rest api client", err = res.error
+ quit(1)
+ let restClient = clientRest.get()
+
+ # start waku node
+ let nodeRes = waitFor initAndStartApp(conf)
+ if nodeRes.isErr():
+ error "could not start node"
+ quit 1
+
+ let (node, discv5) = nodeRes.get()
+
+ (waitFor node.mountRelay()).isOkOr:
+ error "failed to mount waku relay protocol: ", err = error
+ quit 1
+
+ waitFor node.mountLibp2pPing()
+
+ var onFatalErrorAction = proc(msg: string) {.gcsafe, closure.} =
+ ## Action to be taken when an internal error occurs during the node run.
+ ## e.g. the connection with the database is lost and not recovered.
+ error "Unrecoverable error occurred", error = msg
+ quit(QuitFailure)
+
+ if conf.rlnRelay and conf.rlnRelayEthContractAddress != "":
+ let rlnConf = WakuRlnConfig(
+ dynamic: conf.rlnRelayDynamic,
+ credIndex: some(uint(0)),
+ ethContractAddress: conf.rlnRelayEthContractAddress,
+ ethClientUrls: conf.ethClientUrls.mapIt(string(it)),
+ epochSizeSec: conf.rlnEpochSizeSec,
+ creds: none(RlnRelayCreds),
+ onFatalErrorAction: onFatalErrorAction,
+ )
+
+ try:
+ waitFor node.mountRlnRelay(rlnConf)
+ except CatchableError:
+ error "failed to setup RLN", err = getCurrentExceptionMsg()
+ quit 1
+
+ node.mountMetadata(conf.clusterId, conf.shards).isOkOr:
+ error "failed to mount waku metadata protocol: ", err = error
+ quit 1
+
+ for shard in conf.shards:
+ # Subscribe the node to the shards, to count messages
+ subscribeAndHandleMessages(
+ node, $RelayShard(shardId: shard, clusterId: conf.clusterId), msgPerContentTopic
+ )
+
+ # spawn the routine that crawls the network
+ # TODO: split into 3 routines (discovery, connections, ip2location)
+ asyncSpawn crawlNetwork(node, discv5, restClient, conf, allPeersInfo)
+
+ runForever()
diff --git a/third-party/nwaku/apps/networkmonitor/networkmonitor_config.nim b/third-party/nwaku/apps/networkmonitor/networkmonitor_config.nim
new file mode 100644
index 0000000..f67fb09
--- /dev/null
+++ b/third-party/nwaku/apps/networkmonitor/networkmonitor_config.nim
@@ -0,0 +1,190 @@
+import
+ chronicles,
+ chronicles/topics_registry,
+ confutils,
+ chronos,
+ std/strutils,
+ results,
+ regex
+
+const git_version* {.strdefine.} = "n/a"
+
+type EthRpcUrl* = distinct string
+
+proc `$`*(u: EthRpcUrl): string =
+ string(u)
+
+type NetworkMonitorConf* = object
+ logLevel* {.
+ desc: "Sets the log level",
+ defaultValue: LogLevel.INFO,
+ name: "log-level",
+ abbr: "l"
+ .}: LogLevel
+
+ timeout* {.
+ desc: "Timeout to consider that the connection failed",
+ defaultValue: chronos.seconds(10),
+ name: "timeout",
+ abbr: "t"
+ .}: chronos.Duration
+
+ bootstrapNodes* {.
+ desc: "Bootstrap ENR node. Argument may be repeated.",
+ defaultValue: @[""],
+ name: "bootstrap-node",
+ abbr: "b"
+ .}: seq[string]
+
+ dnsDiscoveryUrl* {.
+ desc: "URL for DNS node list in format 'enrtree://@'",
+ defaultValue: "",
+ name: "dns-discovery-url"
+ .}: string
+
+ shards* {.
+ desc:
+ "Shards index to subscribe to [0..NUM_SHARDS_IN_NETWORK-1]. Argument may be repeated.",
+ name: "shard"
+ .}: seq[uint16]
+
+ numShardsInNetwork* {.
+ desc: "Number of shards in the network",
+ name: "num-shards-in-network",
+ defaultValue: 8
+ .}: uint32
+
+ refreshInterval* {.
+ desc: "How often new peers are discovered and connected to (in seconds)",
+ defaultValue: 5,
+ name: "refresh-interval",
+ abbr: "r"
+ .}: int
+
+ clusterId* {.
+ desc:
+ "Cluster id that the node is running in. Node in a different cluster id is disconnected.",
+ defaultValue: 1,
+ name: "cluster-id"
+ .}: uint16
+
+ rlnRelay* {.
+ desc: "Enable spam protection through rln-relay: true|false",
+ defaultValue: true,
+ name: "rln-relay"
+ .}: bool
+
+ rlnRelayDynamic* {.
+ desc: "Enable waku-rln-relay with on-chain dynamic group management: true|false",
+ defaultValue: true,
+ name: "rln-relay-dynamic"
+ .}: bool
+
+ ethClientUrls* {.
+ desc:
+ "HTTP address of an Ethereum testnet client e.g., http://localhost:8540/. Argument may be repeated.",
+ defaultValue: newSeq[EthRpcUrl](0),
+ name: "rln-relay-eth-client-address"
+ .}: seq[EthRpcUrl]
+
+ rlnRelayEthContractAddress* {.
+ desc: "Address of membership contract on an Ethereum testnet",
+ defaultValue: "",
+ name: "rln-relay-eth-contract-address"
+ .}: string
+
+ rlnEpochSizeSec* {.
+ desc:
+ "Epoch size in seconds used to rate limit RLN memberships. Default is 1 second.",
+ defaultValue: 1,
+ name: "rln-relay-epoch-sec"
+ .}: uint64
+
+ rlnRelayUserMessageLimit* {.
+ desc:
+ "Set a user message limit for the rln membership registration. Must be a positive integer. Default is 1.",
+ defaultValue: 1,
+ name: "rln-relay-user-message-limit"
+ .}: uint64
+
+ ## Prometheus metrics config
+ metricsServer* {.
+ desc: "Enable the metrics server: true|false",
+ defaultValue: true,
+ name: "metrics-server"
+ .}: bool
+
+ metricsServerAddress* {.
+ desc: "Listening address of the metrics server.",
+ defaultValue: parseIpAddress("127.0.0.1"),
+ name: "metrics-server-address"
+ .}: IpAddress
+
+ metricsServerPort* {.
+ desc: "Listening HTTP port of the metrics server.",
+ defaultValue: 8008,
+ name: "metrics-server-port"
+ .}: uint16
+
+ ## Custom metrics rest server
+ metricsRestAddress* {.
+ desc: "Listening address of the metrics rest server.",
+ defaultValue: "127.0.0.1",
+ name: "metrics-rest-address"
+ .}: string
+ metricsRestPort* {.
+ desc: "Listening HTTP port of the metrics rest server.",
+ defaultValue: 8009,
+ name: "metrics-rest-port"
+ .}: uint16
+
+proc parseCmdArg*(T: type IpAddress, p: string): T =
+ try:
+ result = parseIpAddress(p)
+ except CatchableError as e:
+ raise newException(ValueError, "Invalid IP address")
+
+proc completeCmdArg*(T: type IpAddress, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type chronos.Duration, p: string): T =
+ try:
+ result = chronos.seconds(parseInt(p))
+ except CatchableError as e:
+ raise newException(ValueError, "Invalid duration value")
+
+proc completeCmdArg*(T: type chronos.Duration, val: string): seq[string] =
+ return @[]
+
+proc completeCmdArg*(T: type EthRpcUrl, val: string): seq[string] =
+ return @[]
+
+proc parseCmdArg*(T: type EthRpcUrl, s: string): T =
+ ## allowed patterns:
+ ## http://url:port
+ ## https://url:port
+ ## http://url:port/path
+ ## https://url:port/path
+ ## http://url/with/path
+ ## http://url:port/path?query
+ ## https://url:port/path?query
+ ## disallowed patterns:
+ ## any valid/invalid ws or wss url
+ var httpPattern =
+ re2"^(https?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*"
+ var wsPattern =
+ re2"^(wss?):\/\/((localhost)|([\w_-]+(?:(?:\.[\w_-]+)+)))(:[0-9]{1,5})?([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])*"
+ if regex.match(s, wsPattern):
+ raise newException(
+ ValueError, "Websocket RPC URL is not supported, Please use an HTTP URL"
+ )
+ if not regex.match(s, httpPattern):
+ raise newException(ValueError, "Invalid HTTP RPC URL")
+ return EthRpcUrl(s)
+
+proc loadConfig*(T: type NetworkMonitorConf): Result[T, string] =
+ try:
+ let conf = NetworkMonitorConf.load(version = git_version)
+ ok(conf)
+ except CatchableError:
+ err(getCurrentExceptionMsg())
diff --git a/third-party/nwaku/apps/networkmonitor/networkmonitor_metrics.nim b/third-party/nwaku/apps/networkmonitor/networkmonitor_metrics.nim
new file mode 100644
index 0000000..dda3e57
--- /dev/null
+++ b/third-party/nwaku/apps/networkmonitor/networkmonitor_metrics.nim
@@ -0,0 +1,107 @@
+{.push raises: [].}
+
+import
+ std/[net, json, tables, sequtils],
+ chronicles,
+ chronicles/topics_registry,
+ chronos,
+ json_serialization,
+ metrics,
+ metrics/chronos_httpserver,
+ presto/route,
+ presto/server,
+ results
+
+logScope:
+ topics = "networkmonitor_metrics"
+
+# On top of our custom metrics, the following are reused from nim-eth
+#routing_table_nodes{state=""}
+#routing_table_nodes{state="seen"}
+#discovery_message_requests_outgoing_total{response=""}
+#discovery_message_requests_outgoing_total{response="no_response"}
+
+declarePublicGauge networkmonitor_peer_type_as_per_enr,
+ "Number of peers supporting each capability according to the ENR",
+ labels = ["capability"]
+
+declarePublicGauge networkmonitor_peer_cluster_as_per_enr,
+ "Number of peers on each cluster according to the ENR", labels = ["cluster"]
+
+declarePublicGauge networkmonitor_peer_type_as_per_protocol,
+ "Number of peers supporting each protocol, after a successful connection) ",
+ labels = ["protocols"]
+
+declarePublicGauge networkmonitor_peer_user_agents,
+ "Number of peers with each user agent", labels = ["user_agent"]
+
+declarePublicHistogram networkmonitor_peer_ping,
+ "Histogram tracking ping durations for discovered peers",
+ buckets = [10.0, 20.0, 50.0, 100.0, 200.0, 300.0, 500.0, 800.0, 1000.0, 2000.0, Inf]
+
+declarePublicGauge networkmonitor_peer_count,
+ "Number of discovered peers", labels = ["connected"]
+
+declarePublicGauge networkmonitor_peer_country_count,
+ "Number of peers per country", labels = ["country"]
+
+type
+ CustomPeerInfo* = object # populated after discovery
+ lastTimeDiscovered*: int64
+ discovered*: int64
+ peerId*: string
+ enr*: string
+ ip*: string
+ enrCapabilities*: seq[string]
+ country*: string
+ city*: string
+ maddrs*: seq[string]
+
+ # only after ok connection
+ lastTimeConnected*: int64
+ retries*: int64
+ supportedProtocols*: seq[string]
+ userAgent*: string
+ lastPingDuration*: Duration
+ avgPingDuration*: Duration
+
+ # only after a ok/nok connection
+ connError*: string
+
+ CustomPeerInfoRef* = ref CustomPeerInfo
+
+ # Stores information about all discovered/connected peers
+ CustomPeersTableRef* = TableRef[string, CustomPeerInfoRef]
+
+ # stores the content topic and the count of rx messages
+ ContentTopicMessageTableRef* = TableRef[string, int]
+
+proc installHandler*(
+ router: var RestRouter,
+ allPeers: CustomPeersTableRef,
+ numMessagesPerContentTopic: ContentTopicMessageTableRef,
+) =
+ router.api(MethodGet, "/allpeersinfo") do() -> RestApiResponse:
+ let values = toSeq(allPeers.values())
+ return RestApiResponse.response(values.toJson(), contentType = "application/json")
+ router.api(MethodGet, "/contenttopics") do() -> RestApiResponse:
+ # TODO: toJson() includes the hash
+ return RestApiResponse.response(
+ $(%numMessagesPerContentTopic), contentType = "application/json"
+ )
+
+proc startMetricsServer*(serverIp: IpAddress, serverPort: Port): Result[void, string] =
+ info "Starting metrics HTTP server", serverIp, serverPort
+
+ try:
+ startMetricsHttpServer($serverIp, serverPort)
+ except Exception as e:
+ error(
+ "Failed to start metrics HTTP server",
+ serverIp = serverIp,
+ serverPort = serverPort,
+ msg = e.msg,
+ )
+
+ info "Metrics HTTP server started", serverIp, serverPort
+ ok()
diff --git a/third-party/nwaku/apps/networkmonitor/networkmonitor_utils.nim b/third-party/nwaku/apps/networkmonitor/networkmonitor_utils.nim
new file mode 100644
index 0000000..0e89c4a
--- /dev/null
+++ b/third-party/nwaku/apps/networkmonitor/networkmonitor_utils.nim
@@ -0,0 +1,53 @@
+{.push raises: [].}
+
+import
+ std/json,
+ results,
+ chronicles,
+ chronicles/topics_registry,
+ chronos,
+ presto/[client, common]
+
+type NodeLocation* = object
+ country*: string
+ city*: string
+ lat*: string
+ long*: string
+ isp*: string
+
+proc flatten*[T](a: seq[seq[T]]): seq[T] =
+ var aFlat = newSeq[T](0)
+ for subseq in a:
+ aFlat &= subseq
+ return aFlat
+
+proc decodeBytes*(
+ t: typedesc[NodeLocation], value: openArray[byte], contentType: Opt[ContentTypeData]
+): RestResult[NodeLocation] =
+ var res: string
+ if len(value) > 0:
+ res = newString(len(value))
+ copyMem(addr res[0], unsafeAddr value[0], len(value))
+ try:
+ let jsonContent = parseJson(res)
+ if $jsonContent["status"].getStr() != "success":
+ error "query failed", result = $jsonContent
+ return err("query failed")
+ return ok(
+ NodeLocation(
+ country: jsonContent["country"].getStr(),
+ city: jsonContent["city"].getStr(),
+ lat: $jsonContent["lat"].getFloat(),
+ long: $jsonContent["lon"].getFloat(),
+ isp: jsonContent["isp"].getStr(),
+ )
+ )
+ except Exception:
+ return err("failed to get the location: " & getCurrentExceptionMsg())
+
+proc encodeString*(value: string): RestResult[string] =
+ ok(value)
+
+proc ipToLocation*(
+ ip: string
+): RestResponse[NodeLocation] {.rest, endpoint: "json/{ip}", meth: MethodGet.}
diff --git a/third-party/nwaku/apps/networkmonitor/nim.cfg b/third-party/nwaku/apps/networkmonitor/nim.cfg
new file mode 100644
index 0000000..2231f2e
--- /dev/null
+++ b/third-party/nwaku/apps/networkmonitor/nim.cfg
@@ -0,0 +1,4 @@
+-d:chronicles_line_numbers
+-d:chronicles_runtime_filtering:on
+-d:discv5_protocol_id:d5waku
+path = "../.."
diff --git a/third-party/nwaku/apps/networkmonitor/prometheus.yaml b/third-party/nwaku/apps/networkmonitor/prometheus.yaml
new file mode 100644
index 0000000..c7af03f
--- /dev/null
+++ b/third-party/nwaku/apps/networkmonitor/prometheus.yaml
@@ -0,0 +1,9 @@
+global:
+ scrape_interval: 15s
+
+scrape_configs:
+ - job_name: 'prometheus'
+ scrape_interval: 5s
+ static_configs:
+ - targets: ['host.docker.internal:8008']
+ metrics_path: '/metrics'
\ No newline at end of file
diff --git a/third-party/nwaku/apps/sonda/.env.example b/third-party/nwaku/apps/sonda/.env.example
new file mode 100644
index 0000000..ea769b3
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/.env.example
@@ -0,0 +1,44 @@
+# RPC URL for accessing testnet via HTTP.
+# e.g. https://linea-sepolia.infura.io/v3/123aa110320f4aec179150fba1e1b1b1
+RLN_RELAY_ETH_CLIENT_ADDRESS=
+
+# Account of testnet where you have Linea Sepolia ETH that would be staked into RLN contract.
+ETH_TESTNET_ACCOUNT=
+
+# Private key of testnet where you have Linea Sepolia ETH that would be staked into RLN contract.
+# Note: make sure you don't use the '0x' prefix.
+# e.g. 0116196e9a8abed42dd1a22eb63fa2a5a17b0c27d716b87ded2c54f1bf192a0b
+ETH_TESTNET_KEY=
+
+# Address of the RLN contract on Linea Sepolia.
+RLN_CONTRACT_ADDRESS=0xB9cd878C90E49F797B4431fBF4fb333108CB90e6
+# Address of the RLN Membership Token contract on Linea Sepolia used to pay for membership.
+TOKEN_CONTRACT_ADDRESS=0x185A0015aC462a0aECb81beCc0497b649a64B9ea
+
+# Password you would like to use to protect your RLN membership.
+RLN_RELAY_CRED_PASSWORD=
+
+# Advanced. Can be left empty in normal use cases.
+NWAKU_IMAGE=
+NODEKEY=
+DOMAIN=
+EXTRA_ARGS=
+STORAGE_SIZE=
+
+
+# -------------------- SONDA CONFIG ------------------
+METRICS_PORT=8004
+NODE_REST_ADDRESS="http://nwaku:8645"
+CLUSTER_ID=16
+SHARD=32
+# Comma separated list of store nodes to poll
+STORE_NODES="/dns4/store-01.do-ams3.shards.test.status.im/tcp/30303/p2p/16Uiu2HAmAUdrQ3uwzuE4Gy4D56hX6uLKEeerJAnhKEHZ3DxF1EfT,
+/dns4/store-02.do-ams3.shards.test.status.im/tcp/30303/p2p/16Uiu2HAm9aDJPkhGxc2SFcEACTFdZ91Q5TJjp76qZEhq9iF59x7R,
+/dns4/store-01.gc-us-central1-a.shards.test.status.im/tcp/30303/p2p/16Uiu2HAmMELCo218hncCtTvC2Dwbej3rbyHQcR8erXNnKGei7WPZ,
+/dns4/store-02.gc-us-central1-a.shards.test.status.im/tcp/30303/p2p/16Uiu2HAmJnVR7ZzFaYvciPVafUXuYGLHPzSUigqAmeNw9nJUVGeM,
+/dns4/store-01.ac-cn-hongkong-c.shards.test.status.im/tcp/30303/p2p/16Uiu2HAm2M7xs7cLPc3jamawkEqbr7cUJX11uvY7LxQ6WFUdUKUT,
+/dns4/store-02.ac-cn-hongkong-c.shards.test.status.im/tcp/30303/p2p/16Uiu2HAm9CQhsuwPR54q27kNj9iaQVfyRzTGKrhFmr94oD8ujU6P"
+# Wait time in seconds between two consecutive queries
+QUERY_DELAY=60
+# Consecutive successful store requests to consider a store node healthy
+HEALTH_THRESHOLD=5
\ No newline at end of file
diff --git a/third-party/nwaku/apps/sonda/.gitignore b/third-party/nwaku/apps/sonda/.gitignore
new file mode 100644
index 0000000..f366b94
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/.gitignore
@@ -0,0 +1,4 @@
+.env
+keystore
+rln_tree
+.env
diff --git a/third-party/nwaku/apps/sonda/Dockerfile.sonda b/third-party/nwaku/apps/sonda/Dockerfile.sonda
new file mode 100644
index 0000000..0e5a606
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/Dockerfile.sonda
@@ -0,0 +1,23 @@
+FROM python:3.9.18-alpine3.18
+
+ENV METRICS_PORT=8004
+ENV NODE_REST_ADDRESS="http://nwaku:8645"
+ENV QUERY_DELAY=60
+ENV STORE_NODES=""
+ENV CLUSTER_ID=1
+ENV SHARD=1
+ENV HEALTH_THRESHOLD=5
+
+WORKDIR /opt
+
+COPY sonda.py /opt/sonda.py
+
+RUN pip install requests argparse prometheus_client
+
+CMD python -u /opt/sonda.py \
+ --metrics-port=$METRICS_PORT \
+ --node-rest-address="${NODE_REST_ADDRESS}" \
+ --delay-seconds=$QUERY_DELAY \
+ --pubsub-topic="/waku/2/rs/${CLUSTER_ID}/${SHARD}" \
+ --store-nodes="${STORE_NODES}" \
+ --health-threshold=$HEALTH_THRESHOLD
diff --git a/third-party/nwaku/apps/sonda/README.md b/third-party/nwaku/apps/sonda/README.md
new file mode 100644
index 0000000..459f6fe
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/README.md
@@ -0,0 +1,52 @@
+# Sonda
+
+Sonda is a tool to monitor store nodes and measure their performance.
+
+It works by running a `nwaku` node, publishing a message from it every fixed interval and performing a store query to all the store nodes we want to monitor to check they respond with the last message we published.
+
+## Instructions
+
+1. Create an `.env` file which will contain the configuration parameters.
+ You can start by copying `.env.example` and adapting it for your use case
+
+ ```
+ cp .env.example .env
+ ${EDITOR} .env
+ ```
+
+ The variables that have to be filled for Sonda are
+
+ ```
+ CLUSTER_ID=
+ SHARD=
+ # Comma separated list of store nodes to poll
+ STORE_NODES=
+ # Wait time in seconds between two consecutive queries
+ QUERY_DELAY=
+ # Consecutive successful store requests to consider a store node healthy
+ HEALTH_THRESHOLD=
+ ```
+
+2. If you want to query nodes in `cluster-id` 1, then you have to follow the steps of registering an RLN membership. Otherwise, you can skip this step.
+
+ For it, you need:
+ * Ethereum Linea Sepolia WebSocket endpoint. Get one free from [Infura](https://linea-sepolia.infura.io/).
+ * Ethereum Linea Sepolia account with minimum 0.01ETH. Get some [here](https://docs.metamask.io/developer-tools/faucet/).
+ * A password to protect your rln membership.
+
+ Fill the `RLN_RELAY_ETH_CLIENT_ADDRESS`, `ETH_TESTNET_KEY` and `RLN_RELAY_CRED_PASSWORD` env variables and run
+
+ ```
+ ./register_rln.sh
+ ```
+
+3. Start Sonda by running
+
+ ```
+ docker-compose up -d
+ ```
+
+4. Browse to http://localhost:3000/dashboards and monitor the performance
+
+ There's two Grafana dashboards: `nwaku-monitoring` to track the stats of your node that is publishing messages and performing queries, and `sonda-monitoring` to monitor the responses of the store nodes.
+
diff --git a/third-party/nwaku/apps/sonda/docker-compose.yml b/third-party/nwaku/apps/sonda/docker-compose.yml
new file mode 100644
index 0000000..d659442
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/docker-compose.yml
@@ -0,0 +1,114 @@
+
+x-logging: &logging
+ logging:
+ driver: json-file
+ options:
+ max-size: 1000m
+
+# Environment variable definitions
+x-rln-relay-eth-client-address: &rln_relay_eth_client_address ${RLN_RELAY_ETH_CLIENT_ADDRESS:-} # Add your RLN_RELAY_ETH_CLIENT_ADDRESS after the "-"
+
+x-rln-environment: &rln_env
+ RLN_RELAY_CONTRACT_ADDRESS: ${RLN_RELAY_CONTRACT_ADDRESS:-0xB9cd878C90E49F797B4431fBF4fb333108CB90e6}
+ RLN_RELAY_CRED_PATH: ${RLN_RELAY_CRED_PATH:-} # Optional: Add your RLN_RELAY_CRED_PATH after the "-"
+ RLN_RELAY_CRED_PASSWORD: ${RLN_RELAY_CRED_PASSWORD:-} # Optional: Add your RLN_RELAY_CRED_PASSWORD after the "-"
+
+x-sonda-env: &sonda_env
+ METRICS_PORT: ${METRICS_PORT:-8004}
+ NODE_REST_ADDRESS: ${NODE_REST_ADDRESS:-"http://nwaku:8645"}
+ CLUSTER_ID: ${CLUSTER_ID:-1}
+ SHARD: ${SHARD:-0}
+ STORE_NODES: ${STORE_NODES:-}
+ QUERY_DELAY: ${QUERY_DELAY-60}
+ HEALTH_THRESHOLD: ${HEALTH_THRESHOLD-5}
+
+# Services definitions
+services:
+ nwaku:
+ image: ${NWAKU_IMAGE:-harbor.status.im/wakuorg/nwaku:deploy-status-prod}
+ container_name: nwaku
+ restart: on-failure
+ ports:
+ - 30304:30304/tcp
+ - 30304:30304/udp
+ - 9005:9005/udp
+ - 127.0.0.1:8003:8003
+ - 80:80 #Let's Encrypt
+ - 8000:8000/tcp #WSS
+ - 127.0.0.1:8645:8645
+ <<:
+ - *logging
+ environment:
+ DOMAIN: ${DOMAIN}
+ NODEKEY: ${NODEKEY}
+ RLN_RELAY_CRED_PASSWORD: "${RLN_RELAY_CRED_PASSWORD}"
+ RLN_RELAY_ETH_CLIENT_ADDRESS: *rln_relay_eth_client_address
+ EXTRA_ARGS: ${EXTRA_ARGS}
+ STORAGE_SIZE: ${STORAGE_SIZE}
+ <<:
+ - *rln_env
+ - *sonda_env
+ volumes:
+ - ./run_node.sh:/opt/run_node.sh:Z
+ - ${CERTS_DIR:-./certs}:/etc/letsencrypt/:Z
+ - ./rln_tree:/etc/rln_tree/:Z
+ - ./keystore:/keystore:Z
+ entrypoint: sh
+ command:
+ - /opt/run_node.sh
+ networks:
+ - nwaku-sonda
+
+ sonda:
+ build:
+ context: .
+ dockerfile: Dockerfile.sonda
+ container_name: sonda
+ ports:
+ - 127.0.0.1:${METRICS_PORT}:${METRICS_PORT}
+ environment:
+ <<:
+ - *sonda_env
+ depends_on:
+ - nwaku
+ networks:
+ - nwaku-sonda
+
+ prometheus:
+ image: docker.io/prom/prometheus:latest
+ container_name: prometheus
+ volumes:
+ - ./monitoring/prometheus-config.yml:/etc/prometheus/prometheus.yml:Z
+ command:
+ - --config.file=/etc/prometheus/prometheus.yml
+ # ports:
+ # - 127.0.0.1:9090:9090
+ restart: on-failure:5
+ depends_on:
+ - nwaku
+ networks:
+ - nwaku-sonda
+
+ grafana:
+ image: docker.io/grafana/grafana:latest
+ container_name: grafana
+ env_file:
+ - ./monitoring/configuration/grafana-plugins.env
+ volumes:
+ - ./monitoring/configuration/grafana.ini:/etc/grafana/grafana.ini:Z
+ - ./monitoring/configuration/dashboards.yaml:/etc/grafana/provisioning/dashboards/dashboards.yaml:Z
+ - ./monitoring/configuration/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml:Z
+ - ./monitoring/configuration/dashboards:/var/lib/grafana/dashboards/:Z
+ - ./monitoring/configuration/customizations/custom-logo.svg:/usr/share/grafana/public/img/grafana_icon.svg:Z
+ - ./monitoring/configuration/customizations/custom-logo.svg:/usr/share/grafana/public/img/grafana_typelogo.svg:Z
+ - ./monitoring/configuration/customizations/custom-logo.png:/usr/share/grafana/public/img/fav32.png:Z
+ ports:
+ - 0.0.0.0:3000:3000
+ restart: on-failure:5
+ depends_on:
+ - prometheus
+ networks:
+ - nwaku-sonda
+
+networks:
+ nwaku-sonda:
\ No newline at end of file
diff --git a/third-party/nwaku/apps/sonda/monitoring/configuration/customizations/custom-logo.png b/third-party/nwaku/apps/sonda/monitoring/configuration/customizations/custom-logo.png
new file mode 100644
index 0000000..dcf13b9
Binary files /dev/null and b/third-party/nwaku/apps/sonda/monitoring/configuration/customizations/custom-logo.png differ
diff --git a/third-party/nwaku/apps/sonda/monitoring/configuration/customizations/custom-logo.svg b/third-party/nwaku/apps/sonda/monitoring/configuration/customizations/custom-logo.svg
new file mode 100644
index 0000000..3c9a6da
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/monitoring/configuration/customizations/custom-logo.svg
@@ -0,0 +1,3 @@
+
diff --git a/third-party/nwaku/apps/sonda/monitoring/configuration/dashboards.yaml b/third-party/nwaku/apps/sonda/monitoring/configuration/dashboards.yaml
new file mode 100644
index 0000000..e59ac96
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/monitoring/configuration/dashboards.yaml
@@ -0,0 +1,9 @@
+apiVersion: 1
+
+providers:
+- name: 'Prometheus'
+ orgId: 1
+ folder: ''
+ type: file
+ options:
+ path: /var/lib/grafana/dashboards
\ No newline at end of file
diff --git a/third-party/nwaku/apps/sonda/monitoring/configuration/dashboards/nwaku-monitoring.json b/third-party/nwaku/apps/sonda/monitoring/configuration/dashboards/nwaku-monitoring.json
new file mode 100644
index 0000000..2b024e3
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/monitoring/configuration/dashboards/nwaku-monitoring.json
@@ -0,0 +1,5303 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "grafana",
+ "uid": "-- Grafana --"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "target": {
+ "limit": 100,
+ "matchAny": false,
+ "tags": [],
+ "type": "dashboard"
+ },
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "gnetId": 12485,
+ "graphTooltip": 0,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 45,
+ "panels": [],
+ "title": "Waku Node",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 9,
+ "x": 0,
+ "y": 1
+ },
+ "id": 41,
+ "options": {
+ "displayMode": "gradient",
+ "maxVizHeight": 300,
+ "minVizHeight": 10,
+ "minVizWidth": 0,
+ "namePlacement": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [],
+ "fields": "",
+ "values": false
+ },
+ "showUnfilled": true,
+ "sizing": "auto",
+ "valueMode": "color"
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "rate(waku_histogram_message_size_bucket[1h])/scalar(rate(waku_histogram_message_size_count[1h]))*100",
+ "format": "heatmap",
+ "instant": true,
+ "legendFormat": "__auto",
+ "range": false,
+ "refId": "A"
+ }
+ ],
+ "title": "Message distrubution %/kBytes (Last Hour)",
+ "type": "bargauge"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "deckbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 9,
+ "x": 9,
+ "y": 1
+ },
+ "id": 38,
+ "options": {
+ "colorMode": "none",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "rate(waku_histogram_message_size_sum[1h])/rate(waku_histogram_message_size_count[1h])",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Average Msg Size (Last Hour)",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "deckbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 5,
+ "x": 9,
+ "y": 5
+ },
+ "id": 42,
+ "options": {
+ "colorMode": "none",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "histogram_quantile(0.75, rate(waku_histogram_message_size_bucket[1h]))",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "75% Percentile (Last hour)",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "deckbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 4,
+ "x": 14,
+ "y": 5
+ },
+ "id": 39,
+ "options": {
+ "colorMode": "none",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "histogram_quantile(0.99, rate(waku_histogram_message_size_bucket[1h]))",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "99% Percentile (Last Hour)",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 9,
+ "x": 0,
+ "y": 9
+ },
+ "id": 12,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "(increase(waku_node_messages_total[1m]))/60",
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Messages/second",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "deckbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 9,
+ "x": 9,
+ "y": 9
+ },
+ "id": 43,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "9.4.7",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "waku_histogram_message_size_sum/waku_histogram_message_size_count",
+ "format": "heatmap",
+ "instant": false,
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Average msg size (kBytes)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "align": "auto",
+ "cellOptions": {
+ "type": "auto"
+ },
+ "inspect": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 3,
+ "x": 0,
+ "y": 16
+ },
+ "id": 2,
+ "options": {
+ "cellHeight": "sm",
+ "footer": {
+ "countRows": false,
+ "fields": "",
+ "reducer": [
+ "sum"
+ ],
+ "show": false
+ },
+ "showHeader": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "builder",
+ "exemplar": false,
+ "expr": "waku_version{instance=\"nwaku:8003\"}",
+ "format": "table",
+ "instant": true,
+ "legendFormat": "__auto",
+ "range": false,
+ "refId": "A"
+ }
+ ],
+ "title": "Version",
+ "transformations": [
+ {
+ "id": "filterFieldsByName",
+ "options": {
+ "include": {
+ "names": [
+ "version"
+ ]
+ }
+ }
+ }
+ ],
+ "type": "table"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 3,
+ "x": 3,
+ "y": 16
+ },
+ "id": 22,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "libp2p_autonat_reachability_confidence",
+ "legendFormat": "{{reachability}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Reachability",
+ "transformations": [
+ {
+ "id": "reduce",
+ "options": {
+ "includeTimeField": false,
+ "mode": "reduceFields",
+ "reducers": [
+ "max"
+ ]
+ }
+ }
+ ],
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 3,
+ "x": 6,
+ "y": 16
+ },
+ "id": 32,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "routing_table_nodes{state=\"seen\"}",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Discv5 (Seen Nodes)",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 3,
+ "x": 9,
+ "y": 16
+ },
+ "id": 33,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "routing_table_nodes",
+ "legendFormat": "{{label_name}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Discv5 (Nodes)",
+ "transformations": [
+ {
+ "id": "filterFieldsByName",
+ "options": {
+ "include": {
+ "names": [
+ "Time",
+ "{__name__=\"routing_table_nodes\", instance=\"nwaku:8003\", job=\"nwaku\"}"
+ ]
+ }
+ }
+ }
+ ],
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 3,
+ "x": 12,
+ "y": 16
+ },
+ "id": 25,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "builder",
+ "expr": "libp2p_peers",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Connected Peers",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 3,
+ "x": 15,
+ "y": 16
+ },
+ "id": 28,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "libp2p_pubsub_topics",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Number Pubsub Topics",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "dateTimeAsIso"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 3,
+ "x": 0,
+ "y": 21
+ },
+ "id": 10,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "process_start_time_seconds{job=\"nwaku\"}*1000",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Start Times (UTC)",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 15,
+ "x": 3,
+ "y": 21
+ },
+ "id": 44,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "waku_connected_peers",
+ "legendFormat": "{{direction}}_{{protocol}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Connected Peers (Direction/Protocol)",
+ "transformations": [],
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 3,
+ "x": 0,
+ "y": 26
+ },
+ "id": 36,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "waku_peer_store_size",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Peer Store Size",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 0,
+ "y": 31
+ },
+ "id": 4,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "9.3.2",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "builder",
+ "expr": "libp2p_peers",
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Connected Peers",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "binBps"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 6,
+ "y": 31
+ },
+ "id": 8,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "rate(libp2p_network_bytes_total{direction=\"in\"}[$__rate_interval])",
+ "legendFormat": "traffic_{{direction}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "libp2p traffic (in)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "binBps"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 12,
+ "y": 31
+ },
+ "id": 29,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "rate(libp2p_network_bytes_total{direction=\"out\"}[$__rate_interval])",
+ "legendFormat": "traffic_{{direction}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "libp2p traffic (out)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "decbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 6,
+ "x": 0,
+ "y": 40
+ },
+ "id": 20,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "nim_gc_heap_instance_occupied_bytes{}",
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Heap allocation",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "decbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 6,
+ "x": 6,
+ "y": 40
+ },
+ "id": 18,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "nim_gc_mem_bytes{}",
+ "hide": false,
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Nim Memory Usage",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 6,
+ "x": 12,
+ "y": 40
+ },
+ "id": 128,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "waku_rln_number_registered_memberships",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "RLN Registered Memberships",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 6,
+ "x": 0,
+ "y": 48
+ },
+ "id": 127,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "waku_rln_proof_generation_duration_seconds",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "RLN Proof Generation (seconds)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 6,
+ "x": 6,
+ "y": 48
+ },
+ "id": 126,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "waku_rln_proof_verification_duration_seconds",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "RLN Proof Verification (seconds)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 6,
+ "x": 12,
+ "y": 48
+ },
+ "id": 135,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "waku_rln_membership_insertion_duration_seconds",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "RLN Membership Insertion (seconds)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 6,
+ "x": 0,
+ "y": 54
+ },
+ "id": 134,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "waku_rln_membership_credentials_import_duration_seconds",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "RLN Credentials Import (seconds)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 6,
+ "x": 6,
+ "y": 54
+ },
+ "id": 137,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "waku_rln_messages_total_total",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "RLN Messages Total",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 6,
+ "x": 12,
+ "y": 54
+ },
+ "id": 136,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "waku_rln_proof_verification_total_total",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "RLN Proof Verification Total",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 6,
+ "x": 0,
+ "y": 60
+ },
+ "id": 133,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "waku_rln_invalid_messages_total_total",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "RLN Invalid Messages Total",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 6,
+ "x": 6,
+ "y": 60
+ },
+ "id": 130,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "waku_rln_spam_messages_total_total",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{__name__}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "RLN Spam Messages Total",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 6,
+ "x": 12,
+ "y": 60
+ },
+ "id": 138,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "waku_rln_invalid_messages_total_total",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{{type}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "RLN Invalid Messages",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Number of messages currently stored in the database",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 9,
+ "x": 0,
+ "y": 66
+ },
+ "id": 141,
+ "options": {
+ "legend": {
+ "calcs": [
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "pg_tb_stats_messages{}",
+ "instant": false,
+ "legendFormat": "{{ pubsubtopic }}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "# messages per shard",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "postgres",
+ "uid": "e5d2e0c2-371d-4178-ac71-edc122fb459c"
+ },
+ "description": "Messages in local database per app name, as extracted from the content topic.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "align": "auto",
+ "cellOptions": {
+ "type": "auto"
+ },
+ "inspect": false
+ },
+ "mappings": [
+ {
+ "options": {
+ "/waku/2/rs/1/0": {
+ "index": 0,
+ "text": "0"
+ },
+ "/waku/2/rs/1/1": {
+ "index": 1,
+ "text": "1"
+ },
+ "/waku/2/rs/1/2": {
+ "index": 2,
+ "text": "2"
+ },
+ "/waku/2/rs/1/3": {
+ "index": 3,
+ "text": "3"
+ },
+ "/waku/2/rs/1/4": {
+ "index": 4,
+ "text": "4"
+ },
+ "/waku/2/rs/1/5": {
+ "index": 5,
+ "text": "5"
+ },
+ "/waku/2/rs/1/6": {
+ "index": 6,
+ "text": "6"
+ },
+ "/waku/2/rs/1/7": {
+ "index": 7,
+ "text": "7"
+ }
+ },
+ "type": "value"
+ }
+ ],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "string"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Number of Messages (sum)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "none"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Total Payload Size (sum)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "decbytes"
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 9,
+ "x": 9,
+ "y": 66
+ },
+ "id": 144,
+ "options": {
+ "cellHeight": "sm",
+ "footer": {
+ "countRows": false,
+ "enablePagination": false,
+ "fields": "",
+ "reducer": [
+ "sum"
+ ],
+ "show": false
+ },
+ "frameIndex": 1,
+ "showHeader": true,
+ "sortBy": []
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "postgres",
+ "uid": "e5d2e0c2-371d-4178-ac71-edc122fb459c"
+ },
+ "editorMode": "code",
+ "format": "table",
+ "hide": false,
+ "rawQuery": true,
+ "rawSql": "SELECT REGEXP_REPLACE(contenttopic,'^\\/(.+)\\/(\\d+)\\/(.+)\\/(.+)$','\\1') as \"App name\", COUNT(id), pg_column_size(payload)\nFROM messages\nGROUP BY contenttopic, payload",
+ "refId": "A",
+ "sql": {
+ "columns": [
+ {
+ "parameters": [
+ {
+ "name": "pubsubtopic",
+ "type": "functionParameter"
+ }
+ ],
+ "type": "function"
+ }
+ ],
+ "groupBy": [
+ {
+ "property": {
+ "name": "pubsubtopic",
+ "type": "string"
+ },
+ "type": "groupBy"
+ }
+ ],
+ "limit": 50
+ },
+ "table": "messages"
+ }
+ ],
+ "title": "Stored Message by Content Topic App Name",
+ "transformations": [
+ {
+ "id": "organize",
+ "options": {
+ "excludeByName": {},
+ "indexByName": {},
+ "renameByName": {
+ "contenttopic": "App name",
+ "count": "Number of Messages",
+ "pg_column_size": "Total Payload Size"
+ }
+ }
+ },
+ {
+ "id": "groupBy",
+ "options": {
+ "fields": {
+ "App name": {
+ "aggregations": [
+ "uniqueValues"
+ ],
+ "operation": "groupby"
+ },
+ "Number of Messages": {
+ "aggregations": [
+ "sum"
+ ],
+ "operation": "aggregate"
+ },
+ "Total Payload Size": {
+ "aggregations": [
+ "sum"
+ ],
+ "operation": "aggregate"
+ },
+ "pg_column_size": {
+ "aggregations": [
+ "sum"
+ ],
+ "operation": "aggregate"
+ }
+ }
+ }
+ },
+ {
+ "id": "sortBy",
+ "options": {
+ "fields": {},
+ "sort": [
+ {
+ "desc": true,
+ "field": "Number of Messages (sum)"
+ }
+ ]
+ }
+ }
+ ],
+ "type": "table"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Number of messages currently stored in the database",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 9,
+ "x": 0,
+ "y": 77
+ },
+ "id": 146,
+ "options": {
+ "legend": {
+ "calcs": [
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "pg_tb_messages_count{}",
+ "instant": false,
+ "interval": "",
+ "legendFormat": "messages",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Unique stored messages (Postgres)",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 84
+ },
+ "id": 46,
+ "panels": [],
+ "title": "Postgres",
+ "type": "row"
+ },
+ {
+ "colorBackground": false,
+ "colorValue": false,
+ "datasource": "Prometheus",
+ "description": "Source: server_version_num",
+ "fieldConfig": {
+ "defaults": {
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 0,
+ "y": 85
+ },
+ "id": 11,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(50, 168, 82)",
+ "show": false
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "expr": "max(pg_settings_server_version_num)",
+ "legendFormat": "",
+ "refId": "A"
+ }
+ ],
+ "thresholds": "",
+ "title": "PostgreSQL Version",
+ "type": "stat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "Prometheus",
+ "description": "Transactions committed + roolback per minute\n\nSource: pg_stat_database,xact_commit + xact_rollback",
+ "fieldConfig": {
+ "defaults": {
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 4,
+ "y": 85
+ },
+ "id": 14,
+ "interval": "",
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "expr": "sum((rate(pg_stat_database_xact_commit{instance=\"$Instance\"}[$Interval])))+sum((rate(pg_stat_database_xact_rollback{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "",
+ "refId": "A"
+ }
+ ],
+ "thresholds": "",
+ "title": "Transaction rate (Postgres)",
+ "type": "stat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "Prometheus",
+ "description": "Statements executed per Minute.\n\nSource: pg_stat_statements.calls",
+ "fieldConfig": {
+ "defaults": {
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 8,
+ "y": 85
+ },
+ "id": 93,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "expr": "sum((rate(pg_stat_statements_calls{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "",
+ "refId": "A"
+ }
+ ],
+ "thresholds": "",
+ "title": "Query rate (Postgres)",
+ "type": "stat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "Prometheus",
+ "description": "Source: pg_stat_statements.total_time / pg_stat_statements.calls",
+ "fieldConfig": {
+ "defaults": {
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "format": "s",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 12,
+ "y": 85
+ },
+ "id": 102,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "expr": "sum((delta(pg_stat_statements_total_time_seconds{instance=\"$Instance\"}[$Interval])))/sum((delta(pg_stat_statements_calls{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "",
+ "refId": "A"
+ }
+ ],
+ "thresholds": "",
+ "title": "Average query runtime (Postgres)",
+ "type": "stat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "Prometheus",
+ "decimals": 2,
+ "description": "Size of all databases in $Instance.\n\nSource: pg_database_size()",
+ "fieldConfig": {
+ "defaults": {
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "decbytes"
+ },
+ "overrides": []
+ },
+ "format": "bytes",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 16,
+ "y": 85
+ },
+ "id": 37,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "expr": "sum(pg_database_size_bytes{instance=\"$Instance\"})",
+ "refId": "A"
+ }
+ ],
+ "thresholds": "",
+ "title": "Total database size (Postgres)",
+ "type": "stat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "Prometheus",
+ "description": "Max Replication lag behind master in seconds\n\nOnly available on a standby system.\n\nSource: pg_last_xact_replay_timestamp\n\nUse: pg_stat_replication for Details.",
+ "fieldConfig": {
+ "defaults": {
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "format": "s",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 20,
+ "y": 85
+ },
+ "id": 84,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "expr": "max(pg_replication_lag{instance=\"$Instance\"})",
+ "interval": "",
+ "legendFormat": "",
+ "refId": "A"
+ }
+ ],
+ "thresholds": "",
+ "title": "Max Replication Lag (Postgres)",
+ "type": "stat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "max"
+ },
+ {
+ "datasource": "Prometheus",
+ "description": "Shared buffer hits vs reads from disc",
+ "fieldConfig": {
+ "defaults": {
+ "decimals": 2,
+ "mappings": [
+ {
+ "id": 0,
+ "op": "=",
+ "text": "N/A",
+ "type": 1,
+ "value": "null"
+ }
+ ],
+ "max": 100,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "semi-dark-red"
+ },
+ {
+ "color": "semi-dark-yellow",
+ "value": 80
+ },
+ {
+ "color": "semi-dark-green",
+ "value": 90
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 3,
+ "x": 0,
+ "y": 88
+ },
+ "id": 16,
+ "links": [],
+ "options": {
+ "minVizHeight": 75,
+ "minVizWidth": 75,
+ "orientation": "horizontal",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showThresholdLabels": false,
+ "showThresholdMarkers": true,
+ "sizing": "auto"
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "expr": "sum(pg_stat_database_blks_hit{instance=~\"$Instance\"})/(sum(pg_stat_database_blks_hit{instance=~\"$Instance\"})+sum(pg_stat_database_blks_read{instance=~\"$Instance\"}))*100",
+ "refId": "A"
+ }
+ ],
+ "title": "Shared Buffer Hits (Postgres)",
+ "type": "gauge"
+ },
+ {
+ "datasource": "Prometheus",
+ "description": "Percentage of max_connections used",
+ "fieldConfig": {
+ "defaults": {
+ "decimals": 0,
+ "mappings": [
+ {
+ "id": 0,
+ "op": "=",
+ "text": "N/A",
+ "type": 1,
+ "value": "null"
+ }
+ ],
+ "max": 1,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "semi-dark-green",
+ "value": null
+ },
+ {
+ "color": "semi-dark-yellow",
+ "value": 0.75
+ },
+ {
+ "color": "semi-dark-red",
+ "value": 0.9
+ }
+ ]
+ },
+ "unit": "percentunit"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 3,
+ "x": 3,
+ "y": 88
+ },
+ "id": 9,
+ "links": [],
+ "options": {
+ "minVizHeight": 75,
+ "minVizWidth": 75,
+ "orientation": "horizontal",
+ "reduceOptions": {
+ "calcs": [
+ "last"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showThresholdLabels": false,
+ "showThresholdMarkers": true,
+ "sizing": "auto"
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "expr": "sum(pg_stat_database_numbackends)/max(pg_settings_max_connections)",
+ "refId": "A"
+ }
+ ],
+ "title": "Connections used (Postgres)",
+ "type": "gauge"
+ },
+ {
+ "datasource": "Prometheus",
+ "description": "Transaction committed vs rollbacked",
+ "fieldConfig": {
+ "defaults": {
+ "decimals": 2,
+ "mappings": [
+ {
+ "id": 0,
+ "op": "=",
+ "text": "N/A",
+ "type": 1,
+ "value": "null"
+ }
+ ],
+ "max": 1,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "semi-dark-red",
+ "value": null
+ },
+ {
+ "color": "#EAB839",
+ "value": 0.75
+ },
+ {
+ "color": "semi-dark-green",
+ "value": 0.9
+ }
+ ]
+ },
+ "unit": "percentunit"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 3,
+ "x": 6,
+ "y": 88
+ },
+ "id": 15,
+ "links": [],
+ "options": {
+ "minVizHeight": 75,
+ "minVizWidth": 75,
+ "orientation": "horizontal",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showThresholdLabels": false,
+ "showThresholdMarkers": true,
+ "sizing": "auto"
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "expr": "sum(pg_stat_database_xact_commit{instance=\"$Instance\"})/(sum(pg_stat_database_xact_commit{instance=\"$Instance\"}) + sum(pg_stat_database_xact_rollback{instance=\"$Instance\"}))",
+ "refId": "A"
+ }
+ ],
+ "title": "Commit Ratio (Postgres)",
+ "type": "gauge"
+ },
+ {
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "Prometheus",
+ "description": "Clients executing Statements.\n\nSource: pg_stat_activity",
+ "fieldConfig": {
+ "defaults": {
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 12,
+ "y": 88
+ },
+ "id": 23,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "expr": "sum(pg_stat_activity_count{state=\"active\",instance=\"$Instance\"})",
+ "refId": "A"
+ }
+ ],
+ "thresholds": "",
+ "title": "Active clients (Postgres)",
+ "type": "stat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "dateTimeAsIso"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 16,
+ "y": 88
+ },
+ "id": 125,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "10.2.3",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "editorMode": "code",
+ "expr": "pg_postmaster_start_time_seconds*1000",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Postgres start time",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "bars",
+ "fillOpacity": 51,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 6,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 91
+ },
+ "id": 142,
+ "options": {
+ "legend": {
+ "calcs": [
+ "last",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "pg_stat_user_tables_n_live_tup{datname=\"postgres\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Live",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "pg_stat_user_tables_n_dead_tup",
+ "fullMetaSearch": false,
+ "hide": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "Dead",
+ "range": true,
+ "refId": "B",
+ "useBackend": false
+ }
+ ],
+ "title": "Estimated number of rows (Postgres)",
+ "type": "timeseries"
+ },
+ {
+ "aliasColors": {},
+ "bars": true,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "decimals": 0,
+ "description": "View: pg_stat_activity",
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 95
+ },
+ "hiddenSeries": false,
+ "id": 24,
+ "interval": "$Interval",
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "hideEmpty": true,
+ "hideZero": true,
+ "max": true,
+ "min": false,
+ "rightSide": false,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": false,
+ "linewidth": 1,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "PostgreSQL Documentation",
+ "url": "https://www.postgresql.org/docs/current/monitoring-stats.html"
+ }
+ ],
+ "nullPointMode": "null as zero",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "10.2.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": true,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "sum by (state) (pg_stat_activity_count{instance=\"$Instance\"})",
+ "legendFormat": "{{state}}",
+ "refId": "A"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "Connections by state (stacked) (Postgres)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "decimals": 0,
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ },
+ {
+ "decimals": 0,
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": true,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "decimals": 0,
+ "description": "View: pg_stat_activity",
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 12,
+ "y": 99
+ },
+ "hiddenSeries": false,
+ "id": 121,
+ "interval": "$Interval",
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "hideEmpty": true,
+ "hideZero": true,
+ "max": true,
+ "min": false,
+ "rightSide": false,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": false,
+ "linewidth": 1,
+ "links": [
+ {
+ "targetBlank": true,
+ "title": "PostgreSQL Documentation",
+ "url": "https://www.postgresql.org/docs/current/monitoring-stats.html"
+ }
+ ],
+ "nullPointMode": "null as zero",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "10.2.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": true,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "sum by (datname) (pg_stat_activity_count{instance=\"$Instance\"})",
+ "legendFormat": "{{datname}}",
+ "refId": "A"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "Connections by database (stacked) (Postgres)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "decimals": 0,
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ },
+ {
+ "decimals": 0,
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "decimals": 2,
+ "description": "1 Minute rate of transactions committed or rollback.",
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 104
+ },
+ "hiddenSeries": false,
+ "id": 122,
+ "interval": "",
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "hideEmpty": false,
+ "hideZero": false,
+ "max": true,
+ "min": false,
+ "rightSide": false,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "10.2.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "sum ((rate(pg_stat_database_xact_commit[$Interval])))",
+ "interval": "",
+ "legendFormat": "committed",
+ "refId": "A"
+ },
+ {
+ "expr": "sum ((rate(pg_stat_database_xact_rollback[$Interval])))",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "rollback",
+ "refId": "B"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "Transactions (Postgres)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "description": "Source: pg_stat_database",
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 12,
+ "y": 108
+ },
+ "hiddenSeries": false,
+ "id": 27,
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "hideZero": false,
+ "max": true,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "10.2.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "sum((rate(pg_stat_database_tup_inserted{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "Inserts",
+ "refId": "A"
+ },
+ {
+ "expr": "sum((rate(pg_stat_database_tup_updated{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "Updates",
+ "refId": "B"
+ },
+ {
+ "expr": "sum((rate(pg_stat_database_tup_deleted{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "Deletes",
+ "refId": "C"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "Tuples inserts/updates/deletes (Postgres)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "none",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "description": "* blk_read_time: Time spent reading data file blocks by backends in this database, in milliseconds\n* blk_write_time: Time spent writing data file blocks by backends in this database, in milliseconds\n\ntrack_io_timings needs to be activated",
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 113
+ },
+ "hiddenSeries": false,
+ "id": 26,
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "max": true,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "10.2.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "sum ((rate(pg_stat_database_blk_read_time{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "blk_read_time",
+ "refId": "A"
+ },
+ {
+ "expr": "sum ((rate(pg_stat_database_blk_write_time{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "blk_read_time",
+ "refId": "B"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "I/O Read/Write time (Postgres)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ms",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "description": "Source: pg_stat_database\n\n* tup_fetched: rows needed to satisfy queries\n* tup_returned: rows read/scanned",
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 12,
+ "y": 117
+ },
+ "hiddenSeries": false,
+ "id": 111,
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "hideZero": false,
+ "max": true,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "10.2.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "sum((rate(pg_stat_database_tup_fetched{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "Fetched",
+ "refId": "A"
+ },
+ {
+ "expr": "sum((rate(pg_stat_database_tup_returned{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "Returned",
+ "refId": "B"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "Tuples fetched/returned (Postgres)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "min": "0",
+ "show": false
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "description": "Source: pg_locks",
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 10,
+ "w": 12,
+ "x": 0,
+ "y": 122
+ },
+ "hiddenSeries": false,
+ "id": 123,
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "max": true,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [
+ {
+ "title": "PostgreSQL Lock Modes",
+ "url": "https://www.postgresql.org/docs/12/explicit-locking.html#LOCKING-TABLES"
+ }
+ ],
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "10.2.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "sum by (mode) (pg_locks_count{instance=\"$Instance\"})",
+ "legendFormat": "{{mode}}",
+ "refId": "A"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "Locks by state (Postgres)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "none",
+ "logBase": 1,
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "description": "Should be 0 \n\nSource: pg_stat_database\n\nWith log_lock_waits turned on, deadlocks will be logged to the PostgreSQL Logfiles.",
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 5,
+ "w": 12,
+ "x": 12,
+ "y": 126
+ },
+ "hiddenSeries": false,
+ "id": 30,
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "hideEmpty": true,
+ "hideZero": true,
+ "max": true,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [
+ {
+ "title": "PostgreSQL Locking",
+ "url": "https://www.postgresql.org/docs/12/explicit-locking.html"
+ }
+ ],
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "10.2.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "sum by (datname) ((rate(pg_stat_database_deadlocks{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "{{datname}}",
+ "refId": "A"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "Deadlocks by database (Postgres)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "none",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "description": "Should be 0. If temporary files are created, it can indicate insufficient work_mem. With log_temp_files the creation of temporary files are logged to the PostgreSQL Logfiles.",
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 5,
+ "w": 12,
+ "x": 12,
+ "y": 131
+ },
+ "hiddenSeries": false,
+ "id": 31,
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "hideEmpty": true,
+ "hideZero": true,
+ "max": true,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [
+ {
+ "title": "PostgreSQL Ressources",
+ "url": "https://www.postgresql.org/docs/current/runtime-config-resource.html"
+ }
+ ],
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "10.2.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "sum by (datname) ((rate(pg_stat_database_temp_files{instance=\"$Instance\"}[$Interval])))",
+ "interval": "",
+ "legendFormat": "{{datname}}",
+ "refId": "A"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "Temporary files by database (Postgres)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "none",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "description": "Lag behind master in seconds.\n\nOnly available on a standby System.",
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 10,
+ "w": 12,
+ "x": 0,
+ "y": 132
+ },
+ "hiddenSeries": false,
+ "id": 120,
+ "interval": "1m",
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "max": true,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "10.2.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "max(pg_replication_lag{instance=\"$Instance\"})",
+ "instant": false,
+ "intervalFactor": 1,
+ "legendFormat": "lag ",
+ "refId": "A"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "Replication lag (Postgres)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "s",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "min": "0",
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ }
+ ],
+ "refresh": "1m",
+ "revision": 1,
+ "schemaVersion": 39,
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "current": {
+ "selected": false,
+ "text": "postgres-exporter:9187",
+ "value": "postgres-exporter:9187"
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "definition": "label_values({job=\"postgres-exporter\"}, instance)",
+ "hide": 0,
+ "includeAll": false,
+ "multi": false,
+ "name": "Instance",
+ "options": [],
+ "query": "label_values({job=\"postgres-exporter\"}, instance)",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "tagValuesQuery": "",
+ "tags": [],
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "current": {
+ "selected": false,
+ "text": "All",
+ "value": "$__all"
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "definition": "label_values(datname)",
+ "hide": 0,
+ "includeAll": true,
+ "multi": true,
+ "name": "Database",
+ "options": [],
+ "query": "label_values(datname)",
+ "refresh": 1,
+ "regex": "/^(?!template*|postgres).*$/",
+ "skipUrlSync": false,
+ "sort": 1,
+ "tagValuesQuery": "",
+ "tags": [],
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "auto": true,
+ "auto_count": 30,
+ "auto_min": "10s",
+ "current": {
+ "selected": false,
+ "text": "10m",
+ "value": "10m"
+ },
+ "hide": 0,
+ "name": "Interval",
+ "options": [
+ {
+ "selected": false,
+ "text": "auto",
+ "value": "$__auto_interval_Interval"
+ },
+ {
+ "selected": false,
+ "text": "30sec",
+ "value": "30sec"
+ },
+ {
+ "selected": false,
+ "text": "1m",
+ "value": "1m"
+ },
+ {
+ "selected": true,
+ "text": "10m",
+ "value": "10m"
+ },
+ {
+ "selected": false,
+ "text": "30m",
+ "value": "30m"
+ },
+ {
+ "selected": false,
+ "text": "1h",
+ "value": "1h"
+ },
+ {
+ "selected": false,
+ "text": "6h",
+ "value": "6h"
+ },
+ {
+ "selected": false,
+ "text": "12h",
+ "value": "12h"
+ },
+ {
+ "selected": false,
+ "text": "1d",
+ "value": "1d"
+ }
+ ],
+ "query": "30sec,1m,10m,30m,1h,6h,12h,1d",
+ "queryValue": "",
+ "refresh": 2,
+ "skipUrlSync": false,
+ "type": "interval"
+ }
+ ]
+ },
+ "time": {
+ "from": "now-15m",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ]
+ },
+ "timezone": "browser",
+ "title": "nwaku-monitoring",
+ "uid": "yns_4vFVk",
+ "version": 1,
+ "weekStart": ""
+}
\ No newline at end of file
diff --git a/third-party/nwaku/apps/sonda/monitoring/configuration/dashboards/sonda-monitoring.json b/third-party/nwaku/apps/sonda/monitoring/configuration/dashboards/sonda-monitoring.json
new file mode 100644
index 0000000..1d87f2b
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/monitoring/configuration/dashboards/sonda-monitoring.json
@@ -0,0 +1,1571 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "grafana",
+ "uid": "-- Grafana --"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Sonda messages successfully sent to the network",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "id": 9,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "successful_sonda_msgs_total",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Sent Sonda Messages",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Sonda messages that failed to be sent to the network",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "{__name__=\"failed_sonda_msgs_total\", instance=\"sonda:8004\", job=\"nwaku\"}"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 0
+ },
+ "id": 2,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "failed_sonda_msgs_total",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Failed Sonda Messages",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Sonda messages successfully sent to the network",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 8
+ },
+ "id": 1,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "increase(successful_sonda_msgs_total[5m])/5",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "interval": "",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Sent Sonda Messages Rate per Minute",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Sonda messages that failed to be sent to the network",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "{instance=\"sonda:8004\", job=\"nwaku\"}"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 8
+ },
+ "id": 10,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "increase(failed_sonda_msgs_total[5m]) / 5",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Failed Sonda Messages per Minute",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Store responses including the latest Sonda message ",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 16
+ },
+ "id": 3,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "successful_store_queries_total{node=~\"^$node$\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{__name__=\"{{__name__}}\" , node=\"{{node}}\"}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Successful Store Responses",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Store queries with a non-200 response",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "{__name__=\"failed_store_queries_total\" , error=\"504 PEER_DIAL_FAILURE: 16Uiu2HAmAUdrQ3uwzuE4Gy4D56hX6uLKEeerJAnhKEHZ3DxF1EfT\", node=\"/dns4/store-01.do-ams3.shards.test.status.im/tcp/30303/p2p/16Uiu2HAmAUdrQ3uwzuE4Gy4D56hX6uLKEeerJAnhKEHZ3DxF1EfT\"}"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 16
+ },
+ "id": 4,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "failed_store_queries_total{node=~\"^$node$\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{__name__=\"{{__name__}}\" , error=\"{{error}}\", node=\"{{node}}\"}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Failed Store Queries",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Store responses including the latest Sonda message ",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "{__name__=\"\" , node=\"/dns4/store-01.do-ams3.shards.test.status.im/tcp/30303/p2p/16Uiu2HAmAUdrQ3uwzuE4Gy4D56hX6uLKEeerJAnhKEHZ3DxF1EfT\"}"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 24
+ },
+ "id": 11,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "increase(successful_store_queries_total{node=~\"^$node$\"}[5m]) / 5",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{__name__=\"{{__name__}}\" , node=\"{{node}}\"}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Successful Store Responses per Minute",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Store queries with a non-200 response",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 24
+ },
+ "id": 12,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "increase(failed_store_queries_total{node=~\"^$node$\"}[5m]) / 5",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{__name__=\"{{__name__}}\" , error=\"{{error}}\", node=\"{{node}}\"}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Failed Store Queries per Minute",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Store responses that didn't include our latest Sonda message",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "{__name__=\"empty_store_responses_total\" , node=\"/dns4/store-01.do-ams3.shards.test.status.im/tcp/30303/p2p/16Uiu2HAmAUdrQ3uwzuE4Gy4D56hX6uLKEeerJAnhKEHZ3DxF1EfT\"}"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 32
+ },
+ "id": 5,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "empty_store_responses_total{node=~\"^$node$\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{__name__=\"{{__name__}}\" , node=\"{{node}}\"}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Empty Store Responses",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Store responses that didn't include our latest Sonda message",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "{__name__=\"\" , node=\"/dns4/store-01.do-ams3.shards.test.status.im/tcp/30303/p2p/16Uiu2HAmAUdrQ3uwzuE4Gy4D56hX6uLKEeerJAnhKEHZ3DxF1EfT\"}"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 32
+ },
+ "id": 13,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "increase(empty_store_responses_total{node=~\"^$node$\"}[5m]) / 5",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{__name__=\"{{__name__}}\" , node=\"{{node}}\"}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Empty Store Responses per Minute",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Latency of store queries",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineWidth": 1
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "{__name__=\"store_query_latency\" , node=\"/dns4/store-01.do-ams3.shards.test.status.im/tcp/30303/p2p/16Uiu2HAmAUdrQ3uwzuE4Gy4D56hX6uLKEeerJAnhKEHZ3DxF1EfT\"}"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 40
+ },
+ "id": 7,
+ "options": {
+ "bucketOffset": 0,
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "store_query_latency{node=~\"^$node$\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{__name__=\"{{__name__}}\" , node=\"{{node}}\"}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Store Query Latency (seconds)",
+ "type": "histogram"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Latency of each store query",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "{__name__=\"store_query_latency\" , node=\"/dns4/store-01.do-ams3.shards.test.status.im/tcp/30303/p2p/16Uiu2HAmAUdrQ3uwzuE4Gy4D56hX6uLKEeerJAnhKEHZ3DxF1EfT\"}"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 40
+ },
+ "id": 6,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "store_query_latency{node=~\"^$node$\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{__name__=\"{{__name__}}\" , node=\"{{node}}\"}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Store Query Latency (seconds)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "description": "Node health according to the configured health threshold. 1 means healthy, 0 not.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "stepAfter",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "{__name__=\"node_health\" , node=\"/dns4/store-01.do-ams3.shards.test.status.im/tcp/30303/p2p/16Uiu2HAmAUdrQ3uwzuE4Gy4D56hX6uLKEeerJAnhKEHZ3DxF1EfT\"}"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 48
+ },
+ "id": 8,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "expr": "node_health{node=~\"^$node$\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "{__name__=\"{{__name__}}\" , node=\"{{node}}\"}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Node health",
+ "type": "timeseries"
+ }
+ ],
+ "refresh": "",
+ "schemaVersion": 39,
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "current": {
+ "selected": true,
+ "text": "All",
+ "value": "$__all"
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "definition": "label_values(node)",
+ "hide": 0,
+ "includeAll": true,
+ "label": "node",
+ "multi": false,
+ "name": "node",
+ "options": [],
+ "query": {
+ "qryType": 1,
+ "query": "label_values(node)",
+ "refId": "PrometheusVariableQueryEditor-VariableQuery"
+ },
+ "refresh": 2,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "type": "query"
+ }
+ ]
+ },
+ "time": {
+ "from": "now-1h",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "",
+ "title": "sonda-monitoring",
+ "uid": "cbd1b6c8-63d2-41f3-b57b-a776ec8fa23e",
+ "version": 1,
+ "weekStart": ""
+}
\ No newline at end of file
diff --git a/third-party/nwaku/apps/sonda/monitoring/configuration/datasources.yaml b/third-party/nwaku/apps/sonda/monitoring/configuration/datasources.yaml
new file mode 100644
index 0000000..9f4f51f
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/monitoring/configuration/datasources.yaml
@@ -0,0 +1,11 @@
+apiVersion: 1
+
+datasources:
+ - name: Prometheus
+ type: prometheus
+ access: proxy
+ org_id: 1
+ url: http://prometheus:9090
+ is_default: true
+ version: 1
+ editable: true
\ No newline at end of file
diff --git a/third-party/nwaku/apps/sonda/monitoring/configuration/grafana-plugins.env b/third-party/nwaku/apps/sonda/monitoring/configuration/grafana-plugins.env
new file mode 100644
index 0000000..2780809
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/monitoring/configuration/grafana-plugins.env
@@ -0,0 +1,2 @@
+#GF_INSTALL_PLUGINS=grafana-worldmap-panel,grafana-piechart-panel,digrich-bubblechart-panel,yesoreyeram-boomtheme-panel,briangann-gauge-panel,jdbranham-diagram-panel,agenty-flowcharting-panel,citilogics-geoloop-panel,savantly-heatmap-panel,mtanda-histogram-panel,pierosavi-imageit-panel,michaeldmoore-multistat-panel,zuburqan-parity-report-panel,natel-plotly-panel,bessler-pictureit-panel,grafana-polystat-panel,corpglory-progresslist-panel,snuids-radar-panel,fzakaria-simple-config.config.annotations-datasource,vonage-status-panel,snuids-trafficlights-panel,pr0ps-trackmap-panel,alexandra-trackmap-panel,btplc-trend-box-panel
+GF_INSTALL_PLUGINS=grafana-worldmap-panel,grafana-piechart-panel,yesoreyeram-boomtheme-panel,briangann-gauge-panel,pierosavi-imageit-panel,bessler-pictureit-panel,vonage-status-panel
diff --git a/third-party/nwaku/apps/sonda/monitoring/configuration/grafana.ini b/third-party/nwaku/apps/sonda/monitoring/configuration/grafana.ini
new file mode 100644
index 0000000..f237726
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/monitoring/configuration/grafana.ini
@@ -0,0 +1,51 @@
+instance_name = nwaku dashboard
+
+;[dashboards.json]
+;enabled = true
+;path = /home/git/grafana/grafana-dashboards/dashboards
+
+
+#################################### Auth ##########################
+[auth]
+disable_login_form = false
+
+#################################### Anonymous Auth ##########################
+[auth.anonymous]
+# enable anonymous access
+enabled = true
+
+# specify organization name that should be used for unauthenticated users
+;org_name = Public
+
+# specify role for unauthenticated users
+org_role = Admin
+; org_role = Viewer
+
+;[security]
+;admin_user = ocr
+;admin_password = ocr
+
+;[users]
+# disable user signup / registration
+;allow_sign_up = false
+
+# Set to true to automatically assign new users to the default organization (id 1)
+;auto_assign_org = true
+
+# Default role new users will be automatically assigned (if disabled above is set to true)
+;auto_assign_org_role = Viewer
+
+#################################### SMTP / Emailing ##########################
+;[smtp]
+;enabled = false
+;host = localhost:25
+;user =
+;password =
+;cert_file =
+;key_file =
+;skip_verify = false
+;from_address = admin@grafana.localhost
+
+;[emails]
+;welcome_email_on_sign_up = false
+
diff --git a/third-party/nwaku/apps/sonda/monitoring/prometheus-config.yml b/third-party/nwaku/apps/sonda/monitoring/prometheus-config.yml
new file mode 100644
index 0000000..51e2b50
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/monitoring/prometheus-config.yml
@@ -0,0 +1,10 @@
+global:
+ scrape_interval: 15s
+ evaluation_interval: 15s
+ external_labels:
+ monitor: "Monitoring"
+
+scrape_configs:
+ - job_name: "nwaku"
+ static_configs:
+ - targets: ["nwaku:8003", "sonda:8004"]
diff --git a/third-party/nwaku/apps/sonda/register_rln.sh b/third-party/nwaku/apps/sonda/register_rln.sh
new file mode 100755
index 0000000..4fb373b
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/register_rln.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+
+if test -f ./keystore/keystore.json; then
+ echo "keystore/keystore.json already exists. Use it instead of creating a new one."
+ echo "Exiting"
+ exit 1
+fi
+
+
+if test -f .env; then
+ echo "Using .env file"
+ . $(pwd)/.env
+fi
+
+# TODO: Set nwaku release when ready instead of quay
+
+if test -n "${ETH_CLIENT_ADDRESS}"; then
+ echo "ETH_CLIENT_ADDRESS variable was renamed to RLN_RELAY_ETH_CLIENT_ADDRESS"
+ echo "Please update your .env file"
+ exit 1
+fi
+
+docker run -v $(pwd)/keystore:/keystore/:Z harbor.status.im/wakuorg/nwaku:v0.30.1 generateRlnKeystore \
+--rln-relay-eth-client-address=${RLN_RELAY_ETH_CLIENT_ADDRESS} \
+--rln-relay-eth-private-key=${ETH_TESTNET_KEY} \
+--rln-relay-eth-contract-address=0xB9cd878C90E49F797B4431fBF4fb333108CB90e6 \
+--rln-relay-cred-path=/keystore/keystore.json \
+--rln-relay-cred-password="${RLN_RELAY_CRED_PASSWORD}" \
+--rln-relay-user-message-limit=20 \
+--execute
diff --git a/third-party/nwaku/apps/sonda/run_node.sh b/third-party/nwaku/apps/sonda/run_node.sh
new file mode 100644
index 0000000..e130019
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/run_node.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+echo "I am a nwaku node"
+
+if test -n "${ETH_CLIENT_ADDRESS}" -o ; then
+ echo "ETH_CLIENT_ADDRESS variable was renamed to RLN_RELAY_ETH_CLIENT_ADDRESS"
+ echo "Please update your .env file"
+ exit 1
+fi
+
+if [ -z "${RLN_RELAY_ETH_CLIENT_ADDRESS}" ] && [ "${CLUSTER_ID}" -eq 1 ]; then
+ echo "Missing Eth client address, please refer to README.md for detailed instructions"
+ exit 1
+fi
+
+if [ "${CLUSTER_ID}" -ne 1 ]; then
+ echo "CLUSTER_ID is not equal to 1, clearing RLN configurations"
+ RLN_RELAY_CRED_PATH=""
+ RLN_RELAY_ETH_CLIENT_ADDRESS=""
+ RLN_RELAY_CRED_PASSWORD=""
+fi
+
+MY_EXT_IP=$(wget -qO- https://api4.ipify.org)
+DNS_WSS_CMD=
+
+if [ -n "${DOMAIN}" ]; then
+
+ LETSENCRYPT_PATH=/etc/letsencrypt/live/${DOMAIN}
+
+ if ! [ -d "${LETSENCRYPT_PATH}" ]; then
+ apk add --no-cache certbot
+
+ certbot certonly\
+ --non-interactive\
+ --agree-tos\
+ --no-eff-email\
+ --no-redirect\
+ --email admin@${DOMAIN}\
+ -d ${DOMAIN}\
+ --standalone
+ fi
+
+ if ! [ -e "${LETSENCRYPT_PATH}/privkey.pem" ]; then
+ echo "The certificate does not exist"
+ sleep 60
+ exit 1
+ fi
+
+ WS_SUPPORT="--websocket-support=true"
+ WSS_SUPPORT="--websocket-secure-support=true"
+ WSS_KEY="--websocket-secure-key-path=${LETSENCRYPT_PATH}/privkey.pem"
+ WSS_CERT="--websocket-secure-cert-path=${LETSENCRYPT_PATH}/cert.pem"
+ DNS4_DOMAIN="--dns4-domain-name=${DOMAIN}"
+
+ DNS_WSS_CMD="${WS_SUPPORT} ${WSS_SUPPORT} ${WSS_CERT} ${WSS_KEY} ${DNS4_DOMAIN}"
+fi
+
+if [ -n "${NODEKEY}" ]; then
+ NODEKEY=--nodekey=${NODEKEY}
+fi
+
+if [ "${CLUSTER_ID}" -eq 1 ]; then
+ RLN_RELAY_CRED_PATH=--rln-relay-cred-path=${RLN_RELAY_CRED_PATH:-/keystore/keystore.json}
+fi
+
+if [ -n "${RLN_RELAY_CRED_PASSWORD}" ]; then
+ RLN_RELAY_CRED_PASSWORD=--rln-relay-cred-password="${RLN_RELAY_CRED_PASSWORD}"
+fi
+
+if [ -n "${RLN_RELAY_ETH_CLIENT_ADDRESS}" ]; then
+ RLN_RELAY_ETH_CLIENT_ADDRESS=--rln-relay-eth-client-address="${RLN_RELAY_ETH_CLIENT_ADDRESS}"
+fi
+
+# TO DO: configure bootstrap nodes in env
+
+exec /usr/bin/wakunode\
+ --relay=true\
+ --filter=false\
+ --lightpush=false\
+ --keep-alive=true\
+ --max-connections=150\
+ --cluster-id="${CLUSTER_ID}"\
+ --discv5-discovery=true\
+ --discv5-udp-port=9005\
+ --discv5-enr-auto-update=True\
+ --log-level=DEBUG\
+ --tcp-port=30304\
+ --metrics-server=True\
+ --metrics-server-port=8003\
+ --metrics-server-address=0.0.0.0\
+ --rest=true\
+ --rest-admin=true\
+ --rest-address=0.0.0.0\
+ --rest-port=8645\
+ --rest-allow-origin="waku-org.github.io"\
+ --rest-allow-origin="localhost:*"\
+ --nat=extip:"${MY_EXT_IP}"\
+ --store=false\
+ --pubsub-topic="/waku/2/rs/${CLUSTER_ID}/${SHARD}"\
+ --discv5-bootstrap-node="enr:-QEKuECA0zhRJej2eaOoOPddNcYr7-5NdRwuoLCe2EE4wfEYkAZhFotg6Kkr8K15pMAGyUyt0smHkZCjLeld0BUzogNtAYJpZIJ2NIJpcISnYxMvim11bHRpYWRkcnO4WgAqNiVib290LTAxLmRvLWFtczMuc2hhcmRzLnRlc3Quc3RhdHVzLmltBnZfACw2JWJvb3QtMDEuZG8tYW1zMy5zaGFyZHMudGVzdC5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEC3rRtFQSgc24uWewzXaxTY8hDAHB8sgnxr9k8Rjb5GeSDdGNwgnZfg3VkcIIjKIV3YWt1Mg0"\
+ --discv5-bootstrap-node="enr:-QEcuEAgXDqrYd_TrpUWtn3zmxZ9XPm7O3GS6lV7aMJJOTsbOAAeQwSd_eoHcCXqVzTUtwTyB4855qtbd8DARnExyqHPAYJpZIJ2NIJpcIQihw1Xim11bHRpYWRkcnO4bAAzNi5ib290LTAxLmdjLXVzLWNlbnRyYWwxLWEuc2hhcmRzLnRlc3Quc3RhdHVzLmltBnZfADU2LmJvb3QtMDEuZ2MtdXMtY2VudHJhbDEtYS5zaGFyZHMudGVzdC5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaECxjqgDQ0WyRSOilYU32DA5k_XNlDis3m1VdXkK9xM6kODdGNwgnZfg3VkcIIjKIV3YWt1Mg0"\
+ --discv5-bootstrap-node="enr:-QEcuEAX6Qk-vVAoJLxR4A_4UVogGhvQrqKW4DFKlf8MA1PmCjgowL-LBtSC9BLjXbb8gf42FdDHGtSjEvvWKD10erxqAYJpZIJ2NIJpcIQI2hdMim11bHRpYWRkcnO4bAAzNi5ib290LTAxLmFjLWNuLWhvbmdrb25nLWMuc2hhcmRzLnRlc3Quc3RhdHVzLmltBnZfADU2LmJvb3QtMDEuYWMtY24taG9uZ2tvbmctYy5zaGFyZHMudGVzdC5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDP7CbRk-YKJwOFFM4Z9ney0GPc7WPJaCwGkpNRyla7mCDdGNwgnZfg3VkcIIjKIV3YWt1Mg0"\
+ ${RLN_RELAY_CRED_PATH}\
+ ${RLN_RELAY_CRED_PASSWORD}\
+ ${RLN_RELAY_TREE_PATH}\
+ ${RLN_RELAY_ETH_CLIENT_ADDRESS}\
+ ${DNS_WSS_CMD}\
+ ${NODEKEY}\
+ ${EXTRA_ARGS}
+
diff --git a/third-party/nwaku/apps/sonda/sonda.py b/third-party/nwaku/apps/sonda/sonda.py
new file mode 100644
index 0000000..8b74bd0
--- /dev/null
+++ b/third-party/nwaku/apps/sonda/sonda.py
@@ -0,0 +1,207 @@
+import requests
+import time
+import json
+import os
+import base64
+import sys
+import urllib.parse
+import requests
+import argparse
+from datetime import datetime
+from prometheus_client import Counter, Gauge, start_http_server
+
+# Content topic where Sona messages are going to be sent
+SONDA_CONTENT_TOPIC = '/sonda/2/polls/proto'
+
+# Prometheus metrics
+successful_sonda_msgs = Counter('successful_sonda_msgs', 'Number of successful Sonda messages sent')
+failed_sonda_msgs = Counter('failed_sonda_msgs', 'Number of failed Sonda messages attempts')
+successful_store_queries = Counter('successful_store_queries', 'Number of successful store queries', ['node'])
+failed_store_queries = Counter('failed_store_queries', 'Number of failed store queries', ['node', 'error'])
+empty_store_responses = Counter('empty_store_responses', "Number of store responses without the latest Sonda message", ['node'])
+store_query_latency = Gauge('store_query_latency', 'Latency of the last store query in seconds', ['node'])
+consecutive_successful_responses = Gauge('consecutive_successful_responses', 'Consecutive successful store responses', ['node'])
+node_health = Gauge('node_health', "Binary indicator of a node's health. 1 is healthy, 0 is not", ['node'])
+
+
+# Argparser configuration
+parser = argparse.ArgumentParser(description='')
+parser.add_argument('-m', '--metrics-port', type=int, default=8004, help='Port to expose prometheus metrics.')
+parser.add_argument('-a', '--node-rest-address', type=str, default="http://nwaku:8645", help='Address of the waku node to send messages to.')
+parser.add_argument('-p', '--pubsub-topic', type=str, default='/waku/2/rs/1/0', help='PubSub topic.')
+parser.add_argument('-d', '--delay-seconds', type=int, default=60, help='Delay in seconds between messages.')
+parser.add_argument('-n', '--store-nodes', type=str, required=True, help='Comma separated list of store nodes to query.')
+parser.add_argument('-t', '--health-threshold', type=int, default=5, help='Consecutive successful store requests to consider a store node healthy.')
+args = parser.parse_args()
+
+
+# Logs message including current UTC time
+def log_with_utc(message):
+ utc_time = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
+ print(f"[{utc_time} UTC] {message}")
+
+
+# Sends Sonda message. Returns True if successful, False otherwise
+def send_sonda_msg(rest_address, pubsub_topic, content_topic, timestamp):
+ message = "Hi, I'm Sonda"
+ base64_message = base64.b64encode(message.encode('utf-8')).decode('ascii')
+ body = {
+ 'payload': base64_message,
+ 'contentTopic': content_topic,
+ 'version': 1,
+ 'timestamp': timestamp
+ }
+
+ encoded_pubsub_topic = urllib.parse.quote(pubsub_topic, safe='')
+ url = f'{rest_address}/relay/v1/messages/{encoded_pubsub_topic}'
+ headers = {'content-type': 'application/json'}
+
+ log_with_utc(f'Sending Sonda message via REST: {url} PubSubTopic: {pubsub_topic}, ContentTopic: {content_topic}, timestamp: {timestamp}')
+
+ try:
+ start_time = time.time()
+ response = requests.post(url, json=body, headers=headers, timeout=10)
+ elapsed_seconds = time.time() - start_time
+
+ log_with_utc(f'Response from {rest_address}: status:{response.status_code} content:{response.text} [{elapsed_seconds:.4f} s.]')
+
+ if response.status_code == 200:
+ successful_sonda_msgs.inc()
+ return True
+ else:
+ response.raise_for_status()
+ except requests.RequestException as e:
+ log_with_utc(f'Error sending request: {e}')
+
+ failed_sonda_msgs.inc()
+ return False
+
+
+# We return true if both our node and the queried Store node returned a 200
+# If our message isn't found but we did get a store 200 response, this function still returns true
+def check_store_response(json_response, store_node, timestamp):
+ # Check for the store node status code
+ if json_response.get('statusCode') != 200:
+ error = f"{json_response.get('statusCode')} {json_response.get('statusDesc')}"
+ log_with_utc(f'Failed performing store query {error}')
+ failed_store_queries.labels(node=store_node, error=error).inc()
+ consecutive_successful_responses.labels(node=store_node).set(0)
+
+ return False
+
+ messages = json_response.get('messages')
+ # If there's no message in the response, increase counters and return
+ if not messages:
+ log_with_utc("No messages in store response")
+ empty_store_responses.labels(node=store_node).inc()
+ consecutive_successful_responses.labels(node=store_node).set(0)
+ return True
+
+ # Search for the Sonda message in the returned messages
+ for message in messages:
+ # If message field is missing in current message, continue
+ if not message.get("message"):
+ log_with_utc("Could not retrieve message")
+ continue
+
+ # If a message is found with the same timestamp as sonda message, increase counters and return
+ if timestamp == message.get('message').get('timestamp'):
+ log_with_utc(f'Found Sonda message in store response node={store_node}')
+ successful_store_queries.labels(node=store_node).inc()
+ consecutive_successful_responses.labels(node=store_node).inc()
+ return True
+
+ # If our message wasn't found in the returned messages, increase counter and return
+ empty_store_responses.labels(node=store_node).inc()
+ consecutive_successful_responses.labels(node=store_node).set(0)
+ return True
+
+
+def send_store_query(rest_address, store_node, encoded_pubsub_topic, encoded_content_topic, timestamp):
+ url = f'{rest_address}/store/v3/messages'
+ params = {
+ 'peerAddr': urllib.parse.quote(store_node, safe=''),
+ 'pubsubTopic': encoded_pubsub_topic,
+ 'contentTopics': encoded_content_topic,
+ 'includeData': 'true',
+ 'startTime': timestamp
+ }
+
+ s_time = time.time()
+
+ try:
+ log_with_utc(f'Sending store request to {store_node}')
+ response = requests.get(url, params=params)
+ except Exception as e:
+ log_with_utc(f'Error sending request: {e}')
+ failed_store_queries.labels(node=store_node, error=str(e)).inc()
+ consecutive_successful_responses.labels(node=store_node).set(0)
+ return False
+
+ elapsed_seconds = time.time() - s_time
+ log_with_utc(f'Response from {rest_address}: status:{response.status_code} [{elapsed_seconds:.4f} s.]')
+
+ if response.status_code != 200:
+ failed_store_queries.labels(node=store_node, error=f'{response.status_code} {response.content}').inc()
+ consecutive_successful_responses.labels(node=store_node).set(0)
+ return False
+
+ # Parse REST response into JSON
+ try:
+ json_response = response.json()
+ except Exception as e:
+ log_with_utc(f'Error parsing response JSON: {e}')
+ failed_store_queries.labels(node=store_node, error="JSON parse error").inc()
+ consecutive_successful_responses.labels(node=store_node).set(0)
+ return False
+
+ # Analyze Store response. Return false if response is incorrect or has an error status
+ if not check_store_response(json_response, store_node, timestamp):
+ return False
+
+ store_query_latency.labels(node=store_node).set(elapsed_seconds)
+ return True
+
+
+def send_store_queries(rest_address, store_nodes, pubsub_topic, content_topic, timestamp):
+ log_with_utc(f'Sending store queries. nodes = {store_nodes} timestamp = {timestamp}')
+ encoded_pubsub_topic = urllib.parse.quote(pubsub_topic, safe='')
+ encoded_content_topic = urllib.parse.quote(content_topic, safe='')
+
+ for node in store_nodes:
+ send_store_query(rest_address, node, encoded_pubsub_topic, encoded_content_topic, timestamp)
+
+
+def main():
+ log_with_utc(f'Running Sonda with args={args}')
+
+ store_nodes = []
+ if args.store_nodes is not None:
+ store_nodes = [s.strip() for s in args.store_nodes.split(",")]
+ log_with_utc(f'Store nodes to query: {store_nodes}')
+
+ # Start Prometheus HTTP server at port set by the CLI(default 8004)
+ start_http_server(args.metrics_port)
+
+ while True:
+ timestamp = time.time_ns()
+
+ # Send Sonda message
+ res = send_sonda_msg(args.node_rest_address, args.pubsub_topic, SONDA_CONTENT_TOPIC, timestamp)
+
+ log_with_utc(f'sleeping: {args.delay_seconds} seconds')
+ time.sleep(args.delay_seconds)
+
+ # Only send store query if message was successfully published
+ if(res):
+ send_store_queries(args.node_rest_address, store_nodes, args.pubsub_topic, SONDA_CONTENT_TOPIC, timestamp)
+
+ # Update node health metrics
+ for store_node in store_nodes:
+ if consecutive_successful_responses.labels(node=store_node)._value.get() >= args.health_threshold:
+ node_health.labels(node=store_node).set(1)
+ else:
+ node_health.labels(node=store_node).set(0)
+
+
+main()
diff --git a/third-party/nwaku/apps/wakucanary/README.md b/third-party/nwaku/apps/wakucanary/README.md
new file mode 100644
index 0000000..1140823
--- /dev/null
+++ b/third-party/nwaku/apps/wakucanary/README.md
@@ -0,0 +1,58 @@
+# waku canary tool
+
+Attempts to dial a peer and asserts it supports a given set of protocols.
+
+```console
+./build/wakucanary --help
+Usage:
+
+wakucanary [OPTIONS]...
+
+The following options are available:
+
+ -a, --address Multiaddress of the peer node to attempt to dial.
+ -t, --timeout Timeout to consider that the connection failed [=chronos.seconds(10)].
+ -p, --protocol Protocol required to be supported: store,relay,lightpush,filter (can be used
+ multiple times).
+ -l, --log-level Sets the log level [=LogLevel.DEBUG].
+ -np, --node-port Listening port for waku node [=60000].
+ --websocket-secure-key-path Secure websocket key path: '/path/to/key.txt' .
+ --websocket-secure-cert-path Secure websocket Certificate path: '/path/to/cert.txt' .
+ -c, --cluster-id Cluster ID of the fleet node to check status [Default=1]
+ -s, --shard Shards index to subscribe to topics [ Argument may be repeated ]
+
+```
+
+The tool can be built as:
+
+```console
+$ make wakucanary
+```
+
+And used as follows. A reachable node that supports both `store` and `filter` protocols.
+
+```console
+$ ./build/wakucanary --address=/dns4/node-01.ac-cn-hongkong-c.waku.sandbox.status.im/tcp/30303/p2p/16Uiu2HAmSJvSJphxRdbnigUV5bjRRZFBhTtWFTSyiKaQByCjwmpV --protocol=store --protocol=filter
+$ echo $?
+0
+```
+
+A node that can't be reached.
+```console
+$ ./build/wakucanary --address=/dns4/node-01.ac-cn-hongkong-c.waku.sandbox.status.im/tcp/1000/p2p/16Uiu2HAmSJvSJphxRdbnigUV5bjRRZFBhTtWFTSyiKaQByCjwmpV --protocol=store --protocol=filter
+$ echo $?
+1
+```
+
+Note that a domain name can also be used.
+```console
+$ ./build/wakucanary --address=/dns4/node-01.do-ams3.status.test.status.im/tcp/30303/p2p/16Uiu2HAkukebeXjTQ9QDBeNDWuGfbaSg79wkkhK4vPocLgR6QFDf --protocol=store --protocol=filter
+$ echo $?
+0
+```
+
+Websockets are also supported. The websocket port openned by waku canary is calculated as `$(--node-port) + 1000` (e.g. when you set `-np 60000`, the WS port will be `61000`)
+```console
+$ ./build/wakucanary --address=/ip4/127.0.0.1/tcp/7777/ws/p2p/16Uiu2HAm4ng2DaLPniRoZtMQbLdjYYWnXjrrJkGoXWCoBWAdn1tu --protocol=store --protocol=filter
+$ ./build/wakucanary --address=/ip4/127.0.0.1/tcp/7777/wss/p2p/16Uiu2HAmB6JQpewXScGoQ2syqmimbe4GviLxRwfsR8dCpwaGBPSE --protocol=store --websocket-secure-key-path=MyKey.key --websocket-secure-cert-path=MyCertificate.crt
+```
diff --git a/third-party/nwaku/apps/wakucanary/certsgenerator.nim b/third-party/nwaku/apps/wakucanary/certsgenerator.nim
new file mode 100644
index 0000000..b8a9e9d
--- /dev/null
+++ b/third-party/nwaku/apps/wakucanary/certsgenerator.nim
@@ -0,0 +1,37 @@
+import osproc, os, httpclient, strutils
+
+proc getPublicIP(): string =
+ let client = newHttpClient()
+ try:
+ let response = client.get("http://api.ipify.org")
+ return response.body
+ except Exception as e:
+ echo "Could not fetch public IP: " & e.msg
+ return "127.0.0.1"
+
+# Function to generate a self-signed certificate
+proc generateSelfSignedCertificate*(certPath: string, keyPath: string): int =
+ # Ensure the OpenSSL is installed
+ if findExe("openssl") == "":
+ echo "OpenSSL is not installed or not in the PATH."
+ return 1
+
+ let publicIP = getPublicIP()
+
+ if publicIP != "127.0.0.1":
+ echo "Your public IP address is: ", publicIP
+
+ # Command to generate private key and cert
+ let
+ cmd =
+ "openssl req -x509 -newkey rsa:4096 -keyout " & keyPath & " -out " & certPath &
+ " -sha256 -days 3650 -nodes -subj '/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=" &
+ publicIP & "'"
+ res = execCmd(cmd)
+
+ if res == 0:
+ echo "Successfully generated self-signed certificate and key."
+ else:
+ echo "Failed to generate certificate and key."
+
+ return res
diff --git a/third-party/nwaku/apps/wakucanary/nim.cfg b/third-party/nwaku/apps/wakucanary/nim.cfg
new file mode 100644
index 0000000..2231f2e
--- /dev/null
+++ b/third-party/nwaku/apps/wakucanary/nim.cfg
@@ -0,0 +1,4 @@
+-d:chronicles_line_numbers
+-d:chronicles_runtime_filtering:on
+-d:discv5_protocol_id:d5waku
+path = "../.."
diff --git a/third-party/nwaku/apps/wakucanary/wakucanary.nim b/third-party/nwaku/apps/wakucanary/wakucanary.nim
new file mode 100644
index 0000000..e770028
--- /dev/null
+++ b/third-party/nwaku/apps/wakucanary/wakucanary.nim
@@ -0,0 +1,300 @@
+import
+ std/[strutils, sequtils, tables, strformat],
+ confutils,
+ chronos,
+ chronicles/topics_registry,
+ os
+import
+ libp2p/protocols/ping,
+ libp2p/crypto/[crypto, secp],
+ libp2p/nameresolving/dnsresolver,
+ libp2p/multicodec
+import
+ ./certsgenerator,
+ waku/[waku_enr, node/peer_manager, waku_core, waku_node, factory/builder]
+
+# protocols and their tag
+const ProtocolsTable = {
+ "store": "/vac/waku/store/",
+ "storev3": "/vac/waku/store-query/3",
+ "relay": "/vac/waku/relay/",
+ "lightpush": "/vac/waku/lightpush/",
+ "filter": "/vac/waku/filter-subscribe/2",
+ "filter-push": "/vac/waku/filter-push/",
+ "ipfs-id": "/ipfs/id/",
+ "autonat": "/libp2p/autonat/",
+ "circuit-relay": "/libp2p/circuit/relay/",
+ "metadata": "/vac/waku/metadata/",
+ "rendezvous": "/rendezvous/",
+ "ipfs-ping": "/ipfs/ping/",
+ "peer-exchange": "/vac/waku/peer-exchange/",
+ "mix": "mix/1.0.0",
+}.toTable
+
+const WebSocketPortOffset = 1000
+const CertsDirectory = "./certs"
+
+# cli flags
+type WakuCanaryConf* = object
+ address* {.
+ desc: "Multiaddress of the peer node to attempt to dial",
+ defaultValue: "",
+ name: "address",
+ abbr: "a"
+ .}: string
+
+ timeout* {.
+ desc: "Timeout to consider that the connection failed",
+ defaultValue: chronos.seconds(10),
+ name: "timeout",
+ abbr: "t"
+ .}: chronos.Duration
+
+ protocols* {.
+ desc:
+ "Protocol required to be supported: store,relay,lightpush,filter (can be used multiple times)",
+ name: "protocol",
+ abbr: "p"
+ .}: seq[string]
+
+ logLevel* {.
+ desc: "Sets the log level",
+ defaultValue: LogLevel.INFO,
+ name: "log-level",
+ abbr: "l"
+ .}: LogLevel
+
+ nodePort* {.
+ desc: "Listening port for waku node",
+ defaultValue: 60000,
+ name: "node-port",
+ abbr: "np"
+ .}: uint16
+
+ ## websocket secure config
+ websocketSecureKeyPath* {.
+ desc: "Secure websocket key path: '/path/to/key.txt' ",
+ defaultValue: "",
+ name: "websocket-secure-key-path"
+ .}: string
+
+ websocketSecureCertPath* {.
+ desc: "Secure websocket Certificate path: '/path/to/cert.txt' ",
+ defaultValue: "",
+ name: "websocket-secure-cert-path"
+ .}: string
+
+ ping* {.
+ desc: "Ping the peer node to measure latency", defaultValue: true, name: "ping"
+ .}: bool
+
+ shards* {.
+ desc:
+ "Shards index to subscribe to [0..NUM_SHARDS_IN_NETWORK-1]. Argument may be repeated.",
+ defaultValue: @[],
+ name: "shard",
+ abbr: "s"
+ .}: seq[uint16]
+
+ clusterId* {.
+ desc:
+ "Cluster id that the node is running in. Node in a different cluster id is disconnected.",
+ defaultValue: 1,
+ name: "cluster-id",
+ abbr: "c"
+ .}: uint16
+
+proc parseCmdArg*(T: type chronos.Duration, p: string): T =
+ try:
+ result = chronos.seconds(parseInt(p))
+ except CatchableError:
+ raise newException(ValueError, "Invalid timeout value")
+
+proc completeCmdArg*(T: type chronos.Duration, val: string): seq[string] =
+ return @[]
+
+proc areProtocolsSupported(
+ toValidateProtocols: seq[string], nodeProtocols: seq[string]
+): bool =
+ ## Checks if all toValidateProtocols are contained in nodeProtocols.
+ ## nodeProtocols contains the full list of protocols currently informed by the node under analysis.
+ ## toValidateProtocols contains the protocols, without version number, that we want to check if they are supported by the node.
+ var numOfSupportedProt: int = 0
+
+ for rawProtocol in toValidateProtocols:
+ let protocolTag = ProtocolsTable[rawProtocol]
+ debug "Checking if protocol is supported", expected_protocol_tag = protocolTag
+
+ var protocolSupported = false
+ for nodeProtocol in nodeProtocols:
+ if nodeProtocol.startsWith(protocolTag):
+ info "The node supports the protocol", supported_protocol = nodeProtocol
+ numOfSupportedProt += 1
+ protocolSupported = true
+ break
+
+ if not protocolSupported:
+ error "The node does not support the protocol", expected_protocol = protocolTag
+
+ if numOfSupportedProt == toValidateProtocols.len:
+ return true
+
+ return false
+
+proc pingNode(
+ node: WakuNode, peerInfo: RemotePeerInfo
+): Future[void] {.async, gcsafe.} =
+ try:
+ let conn = await node.switch.dial(peerInfo.peerId, peerInfo.addrs, PingCodec)
+ let pingDelay = await node.libp2pPing.ping(conn)
+ info "Peer response time (ms)", peerId = peerInfo.peerId, ping = pingDelay.millis
+ except CatchableError:
+ var msg = getCurrentExceptionMsg()
+ if msg == "Future operation cancelled!":
+ msg = "timedout"
+ error "Failed to ping the peer", peer = peerInfo, err = msg
+
+proc main(rng: ref HmacDrbgContext): Future[int] {.async.} =
+ let conf: WakuCanaryConf = WakuCanaryConf.load()
+
+ # create dns resolver
+ let
+ nameServers =
+ @[
+ initTAddress(parseIpAddress("1.1.1.1"), Port(53)),
+ initTAddress(parseIpAddress("1.0.0.1"), Port(53)),
+ ]
+ resolver: DnsResolver = DnsResolver.new(nameServers)
+
+ if conf.logLevel != LogLevel.NONE:
+ setLogLevel(conf.logLevel)
+
+ # ensure input protocols are valid
+ for p in conf.protocols:
+ if p notin ProtocolsTable:
+ error "invalid protocol", protocol = p, valid = ProtocolsTable
+ raise newException(ConfigurationError, "Invalid cli flag values" & p)
+
+ info "Cli flags",
+ address = conf.address,
+ timeout = conf.timeout,
+ protocols = conf.protocols,
+ logLevel = conf.logLevel
+
+ let peerRes = parsePeerInfo(conf.address)
+ if peerRes.isErr():
+ error "Couldn't parse 'conf.address'", error = peerRes.error
+ quit(QuitFailure)
+
+ let peer = peerRes.value
+
+ let
+ nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
+ bindIp = parseIpAddress("0.0.0.0")
+ wsBindPort = Port(conf.nodePort + WebSocketPortOffset)
+ nodeTcpPort = Port(conf.nodePort)
+ isWs = peer.addrs[0].contains(multiCodec("ws")).get()
+ isWss = peer.addrs[0].contains(multiCodec("wss")).get()
+ keyPath =
+ if conf.websocketSecureKeyPath.len > 0:
+ conf.websocketSecureKeyPath
+ else:
+ CertsDirectory & "/key.pem"
+ certPath =
+ if conf.websocketSecureCertPath.len > 0:
+ conf.websocketSecureCertPath
+ else:
+ CertsDirectory & "/cert.pem"
+
+ var builder = WakuNodeBuilder.init()
+ builder.withNodeKey(nodeKey)
+
+ let netConfig = NetConfig.init(
+ bindIp = bindIp,
+ bindPort = nodeTcpPort,
+ wsBindPort = some(wsBindPort),
+ wsEnabled = isWs,
+ wssEnabled = isWss,
+ )
+
+ var enrBuilder = EnrBuilder.init(nodeKey)
+
+ enrBuilder.withWakuRelaySharding(
+ RelayShards(clusterId: conf.clusterId, shardIds: conf.shards)
+ ).isOkOr:
+ error "could not initialize ENR with shards", error
+ quit(QuitFailure)
+
+ let recordRes = enrBuilder.build()
+ let record =
+ if recordRes.isErr():
+ error "failed to create enr record", error = recordRes.error
+ quit(QuitFailure)
+ else:
+ recordRes.get()
+
+ if isWss and
+ (conf.websocketSecureKeyPath.len == 0 or conf.websocketSecureCertPath.len == 0):
+ info "WebSocket Secure requires key and certificate. Generating them"
+ if not dirExists(CertsDirectory):
+ createDir(CertsDirectory)
+ if generateSelfSignedCertificate(certPath, keyPath) != 0:
+ error "Error generating key and certificate"
+ quit(QuitFailure)
+
+ builder.withRecord(record)
+ builder.withNetworkConfiguration(netConfig.tryGet())
+ builder.withSwitchConfiguration(
+ secureKey = some(keyPath), secureCert = some(certPath), nameResolver = resolver
+ )
+
+ let node = builder.build().tryGet()
+
+ if conf.ping:
+ try:
+ await mountLibp2pPing(node)
+ except CatchableError:
+ error "failed to mount libp2p ping protocol: " & getCurrentExceptionMsg()
+ quit(QuitFailure)
+
+ node.mountMetadata(conf.clusterId, conf.shards).isOkOr:
+ error "failed to mount metadata protocol", error
+ quit(QuitFailure)
+
+ await node.start()
+
+ var pingFut: Future[bool]
+ if conf.ping:
+ pingFut = pingNode(node, peer).withTimeout(conf.timeout)
+
+ let timedOut = not await node.connectToNodes(@[peer]).withTimeout(conf.timeout)
+ if timedOut:
+ error "Timedout after", timeout = conf.timeout
+ quit(QuitFailure)
+
+ let lp2pPeerStore = node.switch.peerStore
+ let conStatus = node.peerManager.switch.peerStore[ConnectionBook][peer.peerId]
+
+ if conf.ping:
+ discard await pingFut
+
+ if conStatus in [Connected, CanConnect]:
+ let nodeProtocols = lp2pPeerStore[ProtoBook][peer.peerId]
+
+ if not areProtocolsSupported(conf.protocols, nodeProtocols):
+ error "Not all protocols are supported",
+ expected = conf.protocols, supported = nodeProtocols
+ quit(QuitFailure)
+ elif conStatus == CannotConnect:
+ error "Could not connect", peerId = peer.peerId
+ quit(QuitFailure)
+ return 0
+
+when isMainModule:
+ let rng = crypto.newRng()
+ let status = waitFor main(rng)
+ if status == 0:
+ info "The node is reachable and supports all specified protocols"
+ else:
+ error "The node has some problems (see logs)"
+ quit status
diff --git a/third-party/nwaku/apps/wakunode2/nim.cfg b/third-party/nwaku/apps/wakunode2/nim.cfg
new file mode 100644
index 0000000..a6fab9c
--- /dev/null
+++ b/third-party/nwaku/apps/wakunode2/nim.cfg
@@ -0,0 +1,10 @@
+-d:chronicles_line_numbers
+-d:discv5_protocol_id="d5waku"
+-d:chronicles_runtime_filtering=on
+-d:chronicles_sinks="textlines,json"
+-d:chronicles_default_output_device=dynamic
+# Disabling the following topics from nim-eth and nim-dnsdisc since some types cannot be serialized
+-d:chronicles_disabled_topics="eth,dnsdisc.client"
+# Results in empty output for some reason
+#-d:"chronicles_enabled_topics=GossipSub:TRACE,WakuRelay:TRACE"
+path = "../.."
diff --git a/third-party/nwaku/apps/wakunode2/wakunode2.nim b/third-party/nwaku/apps/wakunode2/wakunode2.nim
new file mode 100644
index 0000000..ac6b38a
--- /dev/null
+++ b/third-party/nwaku/apps/wakunode2/wakunode2.nim
@@ -0,0 +1,102 @@
+{.push raises: [].}
+
+import
+ std/[options, strutils, sequtils, net],
+ chronicles,
+ chronos,
+ metrics,
+ libbacktrace,
+ system/ansi_c,
+ libp2p/crypto/crypto
+import
+ ../../tools/[rln_keystore_generator/rln_keystore_generator, confutils/cli_args],
+ waku/[
+ common/logging,
+ factory/waku,
+ node/health_monitor,
+ waku_api/rest/builder as rest_server_builder,
+ waku_core/message/default_values,
+ ]
+
+logScope:
+ topics = "wakunode main"
+
+const git_version* {.strdefine.} = "n/a"
+
+{.pop.}
+ # @TODO confutils.nim(775, 17) Error: can raise an unlisted exception: ref IOError
+when isMainModule:
+ ## Node setup happens in 6 phases:
+ ## 1. Set up storage
+ ## 2. Initialize node
+ ## 3. Mount and initialize configured protocols
+ ## 4. Start node and mounted protocols
+ ## 5. Start monitoring tools and external interfaces
+ ## 6. Setup graceful shutdown hooks
+
+ const versionString = "version / git commit hash: " & waku.git_version
+
+ var wakuNodeConf = WakuNodeConf.load(version = versionString).valueOr:
+ error "failure while loading the configuration", error = error
+ quit(QuitFailure)
+
+ ## Also called within Waku.new. The call to startRestServerEssentials needs the following line
+ logging.setupLog(wakuNodeConf.logLevel, wakuNodeConf.logFormat)
+
+ case wakuNodeConf.cmd
+ of generateRlnKeystore:
+ let conf = wakuNodeConf.toKeystoreGeneratorConf()
+ doRlnKeystoreGenerator(conf)
+ of noCommand:
+ let conf = wakuNodeConf.toWakuConf().valueOr:
+ error "Waku configuration failed", error = error
+ quit(QuitFailure)
+
+ var waku = (waitFor Waku.new(conf)).valueOr:
+ error "Waku initialization failed", error = error
+ quit(QuitFailure)
+
+ (waitFor startWaku(addr waku)).isOkOr:
+ error "Starting waku failed", error = error
+ quit(QuitFailure)
+
+ debug "Setting up shutdown hooks"
+ proc asyncStopper(waku: Waku) {.async: (raises: [Exception]).} =
+ await waku.stop()
+ quit(QuitSuccess)
+
+ # Handle Ctrl-C SIGINT
+ proc handleCtrlC() {.noconv.} =
+ when defined(windows):
+ # workaround for https://github.com/nim-lang/Nim/issues/4057
+ setupForeignThreadGc()
+ notice "Shutting down after receiving SIGINT"
+ asyncSpawn asyncStopper(waku)
+
+ setControlCHook(handleCtrlC)
+
+ # Handle SIGTERM
+ when defined(posix):
+ proc handleSigterm(signal: cint) {.noconv.} =
+ notice "Shutting down after receiving SIGTERM"
+ asyncSpawn asyncStopper(waku)
+
+ c_signal(ansi_c.SIGTERM, handleSigterm)
+
+ # Handle SIGSEGV
+ when defined(posix):
+ proc handleSigsegv(signal: cint) {.noconv.} =
+ # Require --debugger:native
+ fatal "Shutting down after receiving SIGSEGV", stacktrace = getBacktrace()
+
+ # Not available in -d:release mode
+ writeStackTrace()
+
+ waitFor waku.stop()
+ quit(QuitFailure)
+
+ c_signal(ansi_c.SIGSEGV, handleSigsegv)
+
+ info "Node setup complete"
+
+ runForever()
diff --git a/third-party/nwaku/ci/Jenkinsfile.lpt b/third-party/nwaku/ci/Jenkinsfile.lpt
new file mode 100644
index 0000000..c81a21b
--- /dev/null
+++ b/third-party/nwaku/ci/Jenkinsfile.lpt
@@ -0,0 +1,95 @@
+#!/usr/bin/env groovy
+library 'status-jenkins-lib@v1.8.17'
+
+pipeline {
+ agent { label 'linux' }
+
+ options {
+ timestamps()
+ timeout(time: 20, unit: 'MINUTES')
+ disableRestartFromStage()
+ buildDiscarder(logRotator(
+ numToKeepStr: '10',
+ daysToKeepStr: '30',
+ ))
+ }
+
+ parameters {
+ string(
+ name: 'IMAGE_TAG',
+ description: 'Name of Docker tag to push. Optional Parameter.',
+ defaultValue: 'latest'
+ )
+ string(
+ name: 'IMAGE_NAME',
+ description: 'Name of Docker image to push.',
+ defaultValue: params.IMAGE_NAME ?: 'wakuorg/liteprotocoltester',
+ )
+ string(
+ name: 'DOCKER_CRED',
+ description: 'Name of Docker Registry credential.',
+ defaultValue: params.DOCKER_CRED ?: 'harbor-telemetry-robot',
+ )
+ string(
+ name: 'DOCKER_REGISTRY',
+ description: 'URL of the Docker Registry',
+ defaultValue: params.DOCKER_REGISTRY ?: 'harbor.status.im'
+ )
+ string(
+ name: 'NIMFLAGS',
+ description: 'Flags for Nim compilation.',
+ defaultValue: params.NIMFLAGS ?: [
+ '--colors:off',
+ '-d:disableMarchNative',
+ '-d:chronicles_colors:none',
+ '-d:insecure',
+ ].join(' ')
+ )
+ choice(
+ name: "LOWEST_LOG_LEVEL_ALLOWED",
+ choices: ['TRACE', 'DEGUG', 'INFO', 'NOTICE', 'WARN', 'ERROR', 'FATAL'],
+ description: "Defines the log level, which will be available at runtime (Chronicles log level)"
+ )
+ }
+
+ stages {
+ stage('Build') {
+ steps { script {
+ image = docker.build(
+ "${DOCKER_REGISTRY}/${params.IMAGE_NAME}:${params.IMAGE_TAG ?: env.GIT_COMMIT.take(8)}",
+ "--label=commit='${git.commit()}' " +
+ "--label=version='${git.describe('--tags')}' " +
+ "--build-arg=MAKE_TARGET='liteprotocoltester' " +
+ "--build-arg=NIMFLAGS='${params.NIMFLAGS}' " +
+ "--build-arg=LOG_LEVEL='${params.LOWEST_LOG_LEVEL_ALLOWED}' " +
+ "--target ${params.IMAGE_TAG == 'deploy' ? 'deployment_lpt' : 'standalone_lpt'} " +
+ "--file=apps/liteprotocoltester/Dockerfile.liteprotocoltester.compile " +
+ " ."
+ )
+ } }
+ }
+
+ stage('Check') {
+ steps { script {
+ image.inside('--entrypoint=""') { c ->
+ sh '/usr/bin/liteprotocoltester --version'
+ }
+ } }
+ }
+
+ stage('Push') {
+ when { expression { params.IMAGE_TAG != '' } }
+ steps { script {
+ withDockerRegistry([
+ credentialsId: params.DOCKER_CRED, url: "https://${DOCKER_REGISTRY}"
+ ]) {
+ image.push(params.IMAGE_TAG)
+ }
+ } }
+ }
+ } // stages
+
+ post {
+ cleanup { cleanWs() }
+ } // post
+} // pipeline
diff --git a/third-party/nwaku/ci/Jenkinsfile.prs b/third-party/nwaku/ci/Jenkinsfile.prs
new file mode 100644
index 0000000..2aa5654
--- /dev/null
+++ b/third-party/nwaku/ci/Jenkinsfile.prs
@@ -0,0 +1,137 @@
+#!/usr/bin/env groovy
+
+library 'status-jenkins-lib@v1.6.0'
+
+pipeline {
+ agent { label "${getAgentLabel()} && x86_64" }
+
+ parameters {
+ string(
+ name: 'NIMFLAGS',
+ description: 'Flags for Nim compilation.',
+ defaultValue: params.NIMFLAGS ?: [
+ '--colors:off',
+ '-d:insecure',
+ '-d:disableMarchNative',
+ '--parallelBuild:6',
+ '-d:postgres',
+ ].join(' ')
+ )
+ string(
+ name: 'LOG_LEVEL',
+ description: 'Build logging level. (DEBUG, TRACE)',
+ defaultValue: params.LOG_LEVEL ?: 'DEBUG'
+ )
+ string(
+ name: 'VERBOSITY',
+ description: 'Makefile verbosity level.(0-2)',
+ defaultValue: params.VERBOSITY ?: '1'
+ )
+ string(
+ name: 'MAKEFLAGS',
+ description: 'Makefile flags.',
+ defaultValue: params.MAKEFLAGS ?: '-j6'
+ )
+ }
+
+ options {
+ timestamps()
+ disableRestartFromStage()
+ /* Prevent Jenkins jobs from running forever */
+ timeout(time: 30, unit: 'MINUTES')
+ /* Limit builds retained. */
+ buildDiscarder(logRotator(
+ numToKeepStr: '3',
+ daysToKeepStr: '30',
+ artifactNumToKeepStr: '1',
+ ))
+ }
+
+ environment {
+ TARGET = getAgentLabel()
+ }
+
+ stages {
+ stage('Deps') { steps { script {
+ /* Avoid checking multiple times. */
+ v2changed = versionWasChanged('v2')
+ /* TODO: Re-add caching of Nim compiler. */
+ nix.shell("make ${params.MAKEFLAGS} V=${params.VERBOSITY} update", pure: false)
+ nix.shell("make ${params.MAKEFLAGS} V=${params.VERBOSITY} deps", pure: false)
+ } } }
+
+ stage('Binaries') {
+ parallel {
+ stage('V2') {
+ when { expression { v2changed } }
+ steps { script {
+ nix.shell("make ${params.MAKEFLAGS} NIMFLAGS=\"${params.NIMFLAGS}\" V=${params.VERBOSITY} all")
+ } }
+ }
+ }
+ }
+
+ stage('Run Tests') {
+ parallel {
+ stage('V2') {
+ when { expression { v2changed } }
+ steps { script {
+ nix.shell("make ${params.MAKEFLAGS} NIMFLAGS=\"${params.NIMFLAGS}\" V=${params.VERBOSITY} test")
+ } }
+ }
+ }
+ }
+
+ stage('Upload') {
+ when { expression { v2changed } }
+ steps { script {
+ def out = genOutputFilename()
+ sh "mv build/wakunode2 ${out}"
+ env.PKG_URL = s3.uploadArtifact(out)
+ jenkins.setBuildDesc(Waku: env.PKG_URL)
+ } }
+ }
+ } // stages
+ post {
+ success { script { github.notifyPR(true) } }
+ failure { script { github.notifyPR(false) } }
+ always { cleanWs() }
+ } // post
+} // pipeline
+
+
+/* This allows us to use one Jenkinsfile and run
+ * jobs on different platforms based on job name. */
+def getAgentLabel() {
+ if (params.AGENT_LABEL) {
+ return params.AGENT_LABEL
+ }
+ def tokens = env.JOB_NAME.split('/')
+ for (platform in ['linux', 'macos', 'windows']) {
+ if (tokens.contains(platform)) { return platform }
+ }
+ throw new Exception('No agent provided or found in job path!')
+}
+
+def genOutputFilename() {
+ return [
+ "wakunode2", utils.timestamp(), utils.gitCommit(), getAgentLabel()
+ ].join('-') + (env.NODE_NAME.startsWith('windows') ? '.exe' : '.bin')
+}
+
+def versionWasChanged(version) {
+ def changes = sh(
+ script: "git diff --name-only origin/${env.CHANGE_TARGET}",
+ returnStdout: true
+ )
+ if (changes =~ "(?m)^(Makefile|waku.nimble|config.nims|vendor|ci|shell.nix).*") {
+ return true
+ }
+ if (version == 'v2' && changes =~ "(?m)^(apps|tools)/.*") {
+ return true
+ }
+ if (changes =~ "(?m)^(waku|tests|examples)/(${version}|common)/.*") {
+ return true
+ }
+ return false
+}
diff --git a/third-party/nwaku/ci/Jenkinsfile.release b/third-party/nwaku/ci/Jenkinsfile.release
new file mode 100644
index 0000000..4a0cd0d
--- /dev/null
+++ b/third-party/nwaku/ci/Jenkinsfile.release
@@ -0,0 +1,146 @@
+#!/usr/bin/env groovy
+library 'status-jenkins-lib@v1.8.17'
+
+pipeline {
+ agent { label 'linux' }
+
+ options {
+ timestamps()
+ disableRestartFromStage()
+ timeout(time: 20, unit: 'MINUTES')
+ buildDiscarder(logRotator(
+ numToKeepStr: '10',
+ daysToKeepStr: '30',
+ ))
+ }
+
+ parameters {
+ string(
+ name: 'MAKE_TARGET',
+ description: 'Makefile target to build. Optional Parameter.',
+ defaultValue: params.MAKE_TARGET ?: 'wakunode2',
+ )
+ string(
+ name: 'IMAGE_TAG',
+ description: 'Name of Docker tag to push. Optional Parameter.',
+ defaultValue: getDefaultImageTag()
+ )
+ string(
+ name: 'IMAGE_NAME',
+ description: 'Name of Docker image to push.',
+ defaultValue: params.IMAGE_NAME ?: 'harbor.status.im/wakuorg/nwaku',
+ )
+ string(
+ name: 'DOCKER_CRED',
+ description: 'Name of Docker Registry credential.',
+ defaultValue: params.DOCKER_CRED ?: 'harbor-wakuorg-robot',
+ )
+ string(
+ name: 'DOCKER_REGISTRY_URL',
+ description: 'URL of the Docker Registry',
+ defaultValue: params.DOCKER_REGISTRY_URL ?: 'https://harbor.status.im'
+ )
+ string(
+ name: 'NIMFLAGS',
+ description: 'Flags for Nim compilation.',
+ defaultValue: params.NIMFLAGS ?: [
+ '--colors:off',
+ '-d:disableMarchNative',
+ '-d:chronicles_colors:none',
+ '-d:insecure',
+ ].join(' ')
+ )
+ choice(
+ name: "LOWEST_LOG_LEVEL_ALLOWED",
+ choices: ['TRACE', 'DEGUG', 'INFO', 'NOTICE', 'WARN', 'ERROR', 'FATAL'],
+ description: "Defines the log level, which will be available at runtime (Chronicles log level)",
+ )
+ booleanParam(
+ name: 'DEBUG',
+ description: 'Enable debug features',
+ defaultValue: false
+ )
+ booleanParam(
+ name: 'HEAPTRACK',
+ description: 'Enable heaptrack build',
+ defaultValue: false
+ )
+ }
+
+ stages {
+ stage('Build') {
+ steps { script {
+ if (params.HEAPTRACK) {
+ echo 'Building with heaptrack support'
+ image = docker.build(
+ "${params.IMAGE_NAME}:${params.IMAGE_TAG ?: env.GIT_COMMIT.take(8)}",
+ "--label=build='${env.BUILD_URL}' " +
+ "--label=commit='${git.commit()}' " +
+ "--label=version='${git.describe('--tags')}' " +
+ "--build-arg=MAKE_TARGET='${params.MAKE_TARGET}' " +
+ "--build-arg=NIMFLAGS='${params.NIMFLAGS} -d:postgres -d:heaptracker ' " +
+ "--build-arg=LOG_LEVEL='${params.LOWEST_LOG_LEVEL_ALLOWED}' " +
+ "--build-arg=DEBUG='${params.DEBUG ? "1" : "0"} ' " +
+ "--build-arg=NIM_COMMIT='NIM_COMMIT=heaptrack_support_v2.0.12' " +
+ "--target='debug-with-heaptrack' ."
+ )
+ } else {
+ image = docker.build(
+ "${params.IMAGE_NAME}:${params.IMAGE_TAG ?: env.GIT_COMMIT.take(8)}",
+ "--label=build='${env.BUILD_URL}' " +
+ "--label=commit='${git.commit()}' " +
+ "--label=version='${git.describe('--tags')}' " +
+ "--build-arg=MAKE_TARGET='${params.MAKE_TARGET}' " +
+ "--build-arg=NIMFLAGS='${params.NIMFLAGS} -d:postgres ' " +
+ "--build-arg=LOG_LEVEL='${params.LOWEST_LOG_LEVEL_ALLOWED}' " +
+ "--build-arg=DEBUG='${params.DEBUG ? "1" : "0"} ' " +
+ "--target='prod' ."
+ )
+ }
+ } }
+ }
+
+ stage('Check') {
+ steps { script {
+ image.inside('--entrypoint=""') { c ->
+ sh '/usr/bin/wakunode --version'
+ }
+ } }
+ }
+
+ stage('Push') {
+ when { expression { params.IMAGE_TAG != '' } }
+ steps { script {
+ withDockerRegistry([
+ credentialsId: params.DOCKER_CRED, url: params.DOCKER_REGISTRY_URL
+ ]) {
+ image.push()
+ /* If Git ref is a tag push it as Docker tag too. */
+ if (params.GIT_REF ==~ /v\d+\.\d+\.\d+.*/) {
+ image.push(params.GIT_REF)
+ image.push('latest-release')
+ }
+ }
+ } }
+ }
+ } // stages
+
+ post {
+ success { script {
+ discord.send(
+ header: '**Nim-Waku deployment successful!**',
+ cred: 'discord-waku-deployments-webhook',
+ descPrefix: "Image: [`${IMAGE_NAME}:${IMAGE_TAG}`](https://hub.docker.com/r/${IMAGE_NAME}/tags?name=${IMAGE_TAG})"
+ )
+ } }
+ always { sh 'docker image prune -f' }
+ } // post
+} // pipeline
+
+def getDefaultImageTag() {
+ switch (env.JOB_BASE_NAME) {
+ case 'docker-latest': return 'latest'
+ case 'docker-release': return 'stable'
+ default: return env.JOB_BASE_NAME
+ }
+}
diff --git a/third-party/nwaku/config.nims b/third-party/nwaku/config.nims
new file mode 100644
index 0000000..f74fe18
--- /dev/null
+++ b/third-party/nwaku/config.nims
@@ -0,0 +1,127 @@
+import os
+
+if defined(release):
+ switch("nimcache", "nimcache/release/$projectName")
+else:
+ switch("nimcache", "nimcache/debug/$projectName")
+
+if defined(windows):
+ switch("passL", "rln.lib")
+ switch("define", "postgres=false")
+
+ # Automatically add all vendor subdirectories
+ for dir in walkDir("./vendor"):
+ if dir.kind == pcDir:
+ switch("path", dir.path)
+ switch("path", dir.path / "src")
+
+ # disable timestamps in Windows PE headers - https://wiki.debian.org/ReproducibleBuilds/TimestampsInPEBinaries
+ switch("passL", "-Wl,--no-insert-timestamp")
+ # increase stack size
+ switch("passL", "-Wl,--stack,8388608")
+ # https://github.com/nim-lang/Nim/issues/4057
+ --tlsEmulation:
+ off
+ if defined(i386):
+ # set the IMAGE_FILE_LARGE_ADDRESS_AWARE flag so we can use PAE, if enabled, and access more than 2 GiB of RAM
+ switch("passL", "-Wl,--large-address-aware")
+
+ # The dynamic Chronicles output currently prevents us from using colors on Windows
+ # because these require direct manipulations of the stdout File object.
+ switch("define", "chronicles_colors=off")
+
+# https://github.com/status-im/nimbus-eth2/blob/stable/docs/cpu_features.md#ssse3-supplemental-sse3
+# suggests that SHA256 hashing with SSSE3 is 20% faster than without SSSE3, so
+# given its near-ubiquity in the x86 installed base, it renders a distribution
+# build more viable on an overall broader range of hardware.
+#
+if defined(disableMarchNative):
+ if defined(i386) or defined(amd64):
+ if defined(macosx):
+ # macOS Catalina is EOL as of 2022-09
+ # https://support.apple.com/kb/sp833
+ # "macOS Big Sur - Technical Specifications" lists current oldest
+ # supported models: MacBook (2015 or later), MacBook Air (2013 or later),
+ # MacBook Pro (Late 2013 or later), Mac mini (2014 or later), iMac (2014
+ # or later), iMac Pro (2017 or later), Mac Pro (2013 or later).
+ #
+ # These all have Haswell or newer CPUs.
+ #
+ # This ensures AVX2, AES-NI, PCLMUL, BMI1, and BMI2 instruction set support.
+ switch("passC", "-march=haswell -mtune=generic")
+ switch("passL", "-march=haswell -mtune=generic")
+ else:
+ if defined(marchOptimized):
+ # https://github.com/status-im/nimbus-eth2/blob/stable/docs/cpu_features.md#bmi2--adx
+ switch("passC", "-march=broadwell -mtune=generic")
+ switch("passL", "-march=broadwell -mtune=generic")
+ else:
+ switch("passC", "-mssse3")
+ switch("passL", "-mssse3")
+elif defined(macosx) and defined(arm64):
+ # Apple's Clang can't handle "-march=native" on M1: https://github.com/status-im/nimbus-eth2/issues/2758
+ switch("passC", "-mcpu=apple-m1")
+ switch("passL", "-mcpu=apple-m1")
+else:
+ if not defined(android):
+ switch("passC", "-march=native")
+ switch("passL", "-march=native")
+ if defined(windows):
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65782
+ # ("-fno-asynchronous-unwind-tables" breaks Nim's exception raising, sometimes)
+ switch("passC", "-mno-avx512f")
+ switch("passL", "-mno-avx512f")
+
+--threads:
+ on
+--opt:
+ speed
+--excessiveStackTrace:
+ on
+# enable metric collection
+--define:
+ metrics
+# for heap-usage-by-instance-type metrics and object base-type strings
+--define:
+ nimTypeNames
+
+switch("define", "withoutPCRE")
+
+# the default open files limit is too low on macOS (512), breaking the
+# "--debugger:native" build. It can be increased with `ulimit -n 1024`.
+if not defined(macosx) and not defined(android):
+ # add debugging symbols and original files and line numbers
+ --debugger:
+ native
+ if not (defined(windows) and defined(i386)) and not defined(disable_libbacktrace):
+ # light-weight stack traces using libbacktrace and libunwind
+ --define:
+ nimStackTraceOverride
+ switch("import", "libbacktrace")
+
+--define:
+ nimOldCaseObjects
+ # https://github.com/status-im/nim-confutils/issues/9
+
+# `switch("warning[CaseTransition]", "off")` fails with "Error: invalid command line option: '--warning[CaseTransition]'"
+switch("warning", "CaseTransition:off")
+
+# The compiler doth protest too much, methinks, about all these cases where it can't
+# do its (N)RVO pass: https://github.com/nim-lang/RFCs/issues/230
+switch("warning", "ObservableStores:off")
+
+# Too many false positives for "Warning: method has lock level , but another method has 0 [LockLevel]"
+switch("warning", "LockLevel:off")
+
+if defined(android):
+ var clang = getEnv("ANDROID_COMPILER")
+ var ndk_home = getEnv("ANDROID_TOOLCHAIN_DIR")
+ var sysroot = ndk_home & "/sysroot"
+ var cincludes = sysroot & "/usr/include/" & getEnv("ANDROID_ARCH")
+
+ switch("clang.path", ndk_home & "/bin")
+ switch("clang.exe", clang)
+ switch("clang.linkerexe", clang)
+ switch("passC", "--sysroot=" & sysRoot)
+ switch("passL", "--sysroot=" & sysRoot)
+ switch("cincludes", sysRoot & "/usr/include/")
diff --git a/third-party/nwaku/docker/binaries/Dockerfile.bn.amd64 b/third-party/nwaku/docker/binaries/Dockerfile.bn.amd64
new file mode 100644
index 0000000..7fba7ce
--- /dev/null
+++ b/third-party/nwaku/docker/binaries/Dockerfile.bn.amd64
@@ -0,0 +1,31 @@
+# Dockerfile to build a distributable container image from pre-existing binaries
+FROM debian:bookworm-slim AS prod
+
+ARG MAKE_TARGET=wakunode2
+
+LABEL maintainer="vaclav@status.im"
+LABEL source="https://github.com/waku-org/nwaku"
+LABEL description="Wakunode: Waku client"
+LABEL commit="unknown"
+
+# DevP2P, LibP2P, and JSON RPC ports
+EXPOSE 30303 60000 8545
+
+# Referenced in the binary
+RUN apt-get update &&\
+ apt-get install -y libpq-dev curl iproute2 wget dnsutils &&\
+ apt-get clean && rm -rf /var/lib/apt/lists/*
+
+# Copy to separate location to accomodate different MAKE_TARGET values
+ADD ./build/$MAKE_TARGET /usr/local/bin/
+
+# Copy migration scripts for DB upgrades
+ADD ./migrations/ /app/migrations/
+
+# Symlink the correct wakunode binary
+RUN ln -sv /usr/local/bin/$MAKE_TARGET /usr/bin/wakunode
+
+ENTRYPOINT ["/usr/bin/wakunode"]
+
+# By default just show help if called without arguments
+CMD ["--help"]
diff --git a/third-party/nwaku/docker/binaries/Dockerfile.bn.local b/third-party/nwaku/docker/binaries/Dockerfile.bn.local
new file mode 100644
index 0000000..79445d1
--- /dev/null
+++ b/third-party/nwaku/docker/binaries/Dockerfile.bn.local
@@ -0,0 +1,63 @@
+# Dockerfile to build a distributable container image from pre-existing binaries
+# FROM debian:stable-slim AS prod
+FROM ubuntu:24.04 AS prod
+
+ARG MAKE_TARGET=wakunode2
+
+LABEL maintainer="vaclav@status.im"
+LABEL source="https://github.com/waku-org/nwaku"
+LABEL description="Wakunode: Waku client"
+LABEL commit="unknown"
+
+# DevP2P, LibP2P, and JSON RPC ports
+EXPOSE 30303 60000 8545
+
+# Referenced in the binary
+RUN apt-get update &&\
+ apt-get install -y libpcre3 libpq-dev curl iproute2 wget jq dnsutils &&\
+ apt-get clean && rm -rf /var/lib/apt/lists/*
+
+# Fix for 'Error loading shared library libpcre.so.3: No such file or directory'
+RUN ln -s /usr/lib/libpcre.so /usr/lib/libpcre.so.3
+
+# Copy to separate location to accomodate different MAKE_TARGET values
+ADD ./build/$MAKE_TARGET /usr/local/bin/
+
+# Copy migration scripts for DB upgrades
+ADD ./migrations/ /app/migrations/
+
+# Symlink the correct wakunode binary
+RUN ln -sv /usr/local/bin/$MAKE_TARGET /usr/bin/wakunode
+
+ENTRYPOINT ["/usr/bin/wakunode"]
+
+# By default just show help if called without arguments
+CMD ["--help"]
+
+# Build debug tools: heaptrack
+FROM ubuntu:24.04 AS heaptrack-build
+
+RUN apt update
+RUN apt install -y gdb git g++ make cmake zlib1g-dev libboost-all-dev libunwind-dev
+RUN git clone https://github.com/KDE/heaptrack.git /heaptrack
+
+WORKDIR /heaptrack/build
+# going to a commit that builds properly. We will revisit this for new releases
+RUN git reset --hard f9cc35ebbdde92a292fe3870fe011ad2874da0ca
+RUN cmake -DCMAKE_BUILD_TYPE=Release ..
+RUN make -j$(nproc)
+
+
+# Debug image
+FROM prod AS debug-with-heaptrack
+
+RUN apt update
+RUN apt install -y gdb libunwind8
+
+# Add heaptrack
+COPY --from=heaptrack-build /heaptrack/build/ /heaptrack/build/
+
+ENV LD_LIBRARY_PATH=/heaptrack/build/lib/heaptrack/
+RUN ln -s /heaptrack/build/bin/heaptrack /usr/local/bin/heaptrack
+
+ENTRYPOINT ["/heaptrack/build/bin/heaptrack", "/usr/bin/wakunode"]
diff --git a/third-party/nwaku/docs/api/node.md b/third-party/nwaku/docs/api/node.md
new file mode 100644
index 0000000..ab1580f
--- /dev/null
+++ b/third-party/nwaku/docs/api/node.md
@@ -0,0 +1,94 @@
+# Waku APIs
+
+## Nim API
+
+The Nim Waku API consist of a set of methods operating on the Waku Node object.
+Some of them have different arity depending on what privacy/bandwidth trade-off
+the consumer wants to make. These methods are:
+
+1. **Init** - create a node.
+2. **Start** - start a created node.
+3. **Subscribe** - to a topic or a specific content filter.
+4. **Unsubscribe** - to a topic or a specific content filter.
+5. **Publish** - to a topic, or a topic and a specific content filter.
+6. **Query** - for historical messages.
+7. **Info** - to get information about the node.
+8. **Resume** - to retrieve and persist the message history since the node's last online time.
+
+```Nim
+proc init*(T: type WakuNode, nodeKey: crypto.PrivateKey,
+ bindIp: ValidIpAddress, bindPort: Port,
+ extIp = none[ValidIpAddress](), extPort = none[Port]()): T =
+ ## Creates a Waku Node.
+ ##
+ ## Status: Implemented.
+
+proc start*(node: WakuNode) {.async.} =
+ ## Starts a created Waku Node.
+ ##
+ ## Status: Implemented.
+
+proc subscribe*(node: WakuNode, topic: Topic, handler: TopicHandler) =
+ ## Subscribes to a PubSub topic. Triggers handler when receiving messages on
+ ## this topic. TopicHandler is a method that takes a topic and some data.
+ ##
+ ## NOTE The data field SHOULD be decoded as a WakuMessage.
+ ## Status: Implemented.
+
+proc subscribe*(node: WakuNode, request: FilterRequest, handler: ContentFilterHandler) {.async, gcsafe.} =
+ ## Registers for messages that match a specific filter. Triggers the handler whenever a message is received.
+ ## FilterHandler is a method that takes a MessagePush.
+ ##
+ ## Status: Implemented.
+
+proc unsubscribe*(node: WakuNode, topic: Topic, handler: TopicHandler) =
+ ## Unsubscribes a handler from a PubSub topic.
+ ##
+ ## Status: Implemented.
+
+proc unsubscribeAll*(node: WakuNode, topic: Topic) =
+ ## Unsubscribes all handlers registered on a specific PubSub topic.
+ ##
+ ## Status: Implemented.
+
+proc unsubscribe*(w: WakuNode, contentFilter: ContentFilter) =
+ ## Unsubscribe from a content filter.
+ ##
+ ## Status: Not yet implemented.
+ ## TODO Implement.
+
+proc publish*(node: WakuNode, topic: Topic, message: WakuMessage) =
+ ## Publish a `WakuMessage` to a PubSub topic. `WakuMessage` should contain a
+ ## `contentTopic` field for light node functionality. This field may be also
+ ## be omitted.
+ ##
+ ## Status: Implemented.
+
+proc query*(w: WakuNode, query: HistoryQuery, handler: QueryHandlerFunc) {.async, gcsafe.} =
+ ## Queries known nodes for historical messages. Triggers the handler whenever a response is received.
+ ## QueryHandlerFunc is a method that takes a HistoryResponse.
+ ##
+ ## Status: Implemented.
+
+proc info*(node: WakuNode): WakuInfo =
+ ## Returns information about the Node, such as what multiaddress it can be reached at.
+ ##
+ ## Status: Implemented.
+ ##
+
+proc resume*(node: WakuNode, peerList: Option[seq[PeerInfo]]) =
+ ## Retrieves and persists the history of waku messages published on the default waku pubsub topic since the last time the waku node has been online.
+ ## It requires the waku node to have the store protocol mounted in the full mode (i.e., persisting messages).
+ ## `peerList` indicates the list of peers to query from.
+ ## The history is fetched from all available peers in this list and then consolidated into one deduplicated list.
+ ## If no peerList is passed, the history is fetched from one of the known peers.
+ ## It retrieves the history successfully given that the dialed peer has been online during the queried time window.
+ ##
+ ## Status: Implemented.
+ ##
+```
+
+
+## REST API
+
+[Here](./rest-api.md) you can find more details on the Node HTTP REST API.
diff --git a/third-party/nwaku/docs/api/rest-api.md b/third-party/nwaku/docs/api/rest-api.md
new file mode 100644
index 0000000..eeb90ab
--- /dev/null
+++ b/third-party/nwaku/docs/api/rest-api.md
@@ -0,0 +1,43 @@
+## HTTP REST API
+
+The HTTP REST API consists of a set of methods operating on the Waku Node remotely over HTTP.
+
+This API is divided in different _namespaces_ which group a set of resources:
+
+| Namespace | Description |
+------------|--------------
+| `/debug` | Information about a Waku v2 node. |
+| `/relay` | Control of the relaying of messages. See [11/WAKU2-RELAY](https://rfc.vac.dev/spec/11/) RFC |
+| `/store` | Retrieve the message history. See [13/WAKU2-STORE](https://rfc.vac.dev/spec/13/) RFC |
+| `/filter` | Control of the content filtering. See [12/WAKU2-FILTER](https://rfc.vac.dev/spec/12/) RFC |
+| `/admin` | Privileged access to the internal operations of the node. |
+| `/private` | Provides functionality to encrypt/decrypt `WakuMessage` payloads using either symmetric or asymmetric cryptography. This allows backwards compatibility with Waku v1 nodes. |
+
+
+### API Specification
+
+The HTTP REST API has been designed following the OpenAPI 3.0.3 standard specification format.
+The OpenAPI specification files can be found in the [Waku Node REST API Reference](https://waku-org.github.io/waku-rest-api/) repository.
+
+You can also use [hosted OpenAPI UI](https://waku-org.github.io/waku-rest-api/) to explore and execute the calls locally.
+
+Check the [OpenAPI Tools](https://openapi.tools/) site for the right tool for you (e.g. REST API client generator)
+
+A particular OpenAPI spec can be easily imported into [Postman](https://www.postman.com/downloads/)
+ 1. Open Postman.
+ 2. Click on File -> Import...
+ 2. Load the openapi.yaml of interest, stored in your computer.
+ 3. Then, requests can be made from within the 'Collections' section.
+
+
+### Usage example
+
+#### [`get_waku_v2_debug_v1_info`](https://rfc.vac.dev/spec/16/#get_waku_v2_debug_v1_info)
+
+```bash
+curl http://localhost:8645/debug/v1/info -s | jq
+```
+
+
+### Node configuration
+Find details [here](https://github.com/waku-org/nwaku/tree/master/docs/operators/how-to/configure-rest-api.md)
diff --git a/third-party/nwaku/docs/benchmarks/cspell.json b/third-party/nwaku/docs/benchmarks/cspell.json
new file mode 100644
index 0000000..8227630
--- /dev/null
+++ b/third-party/nwaku/docs/benchmarks/cspell.json
@@ -0,0 +1,20 @@
+{ "words":
+ [
+ "pubsubtopic",
+ "jmeter",
+ "analyzed",
+ "queryc",
+ "wakudev",
+ "statusim",
+ "queryc",
+ "wakudev",
+ "statusim",
+ "chronos",
+ "libpqis",
+ "Conn",
+ "messageindex",
+ "storedat",
+ "pubsubtopic",
+ "wakudev"
+ ]
+}
diff --git a/third-party/nwaku/docs/benchmarks/imgs/digram_multiple_nodes_one_database.png b/third-party/nwaku/docs/benchmarks/imgs/digram_multiple_nodes_one_database.png
new file mode 100644
index 0000000..e26f392
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/digram_multiple_nodes_one_database.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-2.png b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-2.png
new file mode 100644
index 0000000..a5af6a3
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-2.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-3.png b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-3.png
new file mode 100644
index 0000000..34980c7
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-3.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-4.png b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-4.png
new file mode 100644
index 0000000..7287c82
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-4.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-5.png b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-5.png
new file mode 100644
index 0000000..8e97630
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-5.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-6.png b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-6.png
new file mode 100644
index 0000000..de86a96
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-6.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-postgres-2.png b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-postgres-2.png
new file mode 100644
index 0000000..b515c5b
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-postgres-2.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-postgres-3.png b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-postgres-3.png
new file mode 100644
index 0000000..5531572
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-postgres-3.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-postgres.png b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-postgres.png
new file mode 100644
index 0000000..dad6707
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist-postgres.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist.png b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist.png
new file mode 100644
index 0000000..9247b8e
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/insert-time-dist.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/jmeter-results.png b/third-party/nwaku/docs/benchmarks/imgs/jmeter-results.png
new file mode 100644
index 0000000..451c662
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/jmeter-results.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/num-queries-per-minute.png b/third-party/nwaku/docs/benchmarks/imgs/num-queries-per-minute.png
new file mode 100644
index 0000000..f63c197
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/num-queries-per-minute.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-2.png b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-2.png
new file mode 100644
index 0000000..c101c81
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-2.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-3.png b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-3.png
new file mode 100644
index 0000000..078e3ee
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-3.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-4.png b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-4.png
new file mode 100644
index 0000000..a6ea99e
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-4.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-5.png b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-5.png
new file mode 100644
index 0000000..484545e
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-5.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-6.png b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-6.png
new file mode 100644
index 0000000..4bc5ea9
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-6.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-postgres-2.png b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-postgres-2.png
new file mode 100644
index 0000000..3f7033f
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-postgres-2.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-postgres-3.png b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-postgres-3.png
new file mode 100644
index 0000000..2816a8d
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-postgres-3.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-postgres.png b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-postgres.png
new file mode 100644
index 0000000..034ca24
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist-postgres.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/query-time-dist.png b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist.png
new file mode 100644
index 0000000..ff7eca4
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/query-time-dist.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/topology-only-store-protocol.png b/third-party/nwaku/docs/benchmarks/imgs/topology-only-store-protocol.png
new file mode 100644
index 0000000..7ef28c1
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/topology-only-store-protocol.png differ
diff --git a/third-party/nwaku/docs/benchmarks/imgs/using-jmeter.png b/third-party/nwaku/docs/benchmarks/imgs/using-jmeter.png
new file mode 100644
index 0000000..73918de
Binary files /dev/null and b/third-party/nwaku/docs/benchmarks/imgs/using-jmeter.png differ
diff --git a/third-party/nwaku/docs/benchmarks/postgres-adoption.md b/third-party/nwaku/docs/benchmarks/postgres-adoption.md
new file mode 100644
index 0000000..89fba19
--- /dev/null
+++ b/third-party/nwaku/docs/benchmarks/postgres-adoption.md
@@ -0,0 +1,239 @@
+---
+title: PostgreSQL
+description: Document that describes why Nim-Waku started to use Postgres and shows some benchmark and comparison results.
+---
+
+## Introduction
+
+The *Nim Waku Node*, *nwaku*, has the capability of archiving messages until a certain limit (e.g. 30 days) so that other nodes can synchronize their message history throughout the *Store* protocol.
+
+The *nwaku* originally used *SQLite* to archive messages but this has an impact on the node. *Nwaku* is single-threaded and therefore, any *SQLite* operation impacts the performance of other protocols, like *Relay.*
+
+Therefore, the *Postgres* adoption is needed to enhance that.
+
+[https://github.com/waku-org/nwaku/issues/1888](https://github.com/waku-org/nwaku/issues/1888)
+
+## How to connect the *nwaku* to *Postgres*
+
+Simply pass the next parameter to *nwaku*
+
+```bash
+--store-message-db-url="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/postgres
+```
+
+Notice that this only makes sense if the _nwaku_ has the _Store_ protocol mounted
+```bash
+--store=true
+```
+
+(start the _nwaku_ node with `--help` parameter for more _Store_ options)
+
+## Examples of *nwaku* using *Postgres*
+
+[https://github.com/waku-org/nwaku-compose](https://github.com/waku-org/nwaku-compose)
+
+[https://github.com/waku-org/test-waku-query](https://github.com/waku-org/test-waku-query)
+
+## Stress tests
+
+The following repository was created as a tool to stress and compare performance between *nwaku*+*Postgres* and *nwaku*+*SQLite*:
+
+[https://github.com/waku-org/test-waku-query](https://github.com/waku-org/test-waku-query)
+
+### Insert test results
+
+#### Maximum insert throughput
+
+**Scenario**
+
+- 1 node subscribed to pubsubtopic ‘x’ and the *Store* protocol mounted.
+- ‘n’ nodes connected to the “store” node, and publishing messages simultaneously to pubsubtopic ‘x’.
+- All nodes running locally in a *Dell Latitude 7640*.
+- Each published message is fixed, 1.4 KB: [publish_one_client.sh](https://github.com/waku-org/test-waku-query/blob/master/sh/publish_one_client.sh)
+- The next script is used to simulate multiple nodes publishing messages: [publish_multiple_clients.sh](https://github.com/waku-org/test-waku-query/blob/fe7061a21eb14395e723402face755c826077aec/sh/publish_multiple_clients.sh)
+
+**Sought goal**
+
+Find out the maximum number of concurrent inserts that both *SQLite* and *Postgres* could support, and check whether _Postgres_ behaves better than _SQLite_ or not.
+
+**Conclusion**
+
+Messages are lost after a certain threshold, and this message loss is due to limitations in the *Relay* protocol (GossipSub - libp2p.)
+
+For example, if we set 30 nodes publishing 300 messages simultaneously, then 8997 rows were stored and not the expected 9000, in both *SQLite* and *Postgres* databases.
+
+The reason why few messages were lost is because the message rate was higher than the *relay* protocol can support, and therefore a few messages were not stored. In this example, the test took 38.8’’, and therefore, the node was receiving 232 msgs/sec, which is much more than the normal rate a node will work with, which is ~10 msgs/sec (rate extracted from Grafana’s stats for the *status.prod* fleet.)
+
+As a conclusion, the bottleneck is within the *Relay* protocol itself and not the underlying databases. Or, in other words, both *SQLite* and *Postgres* can support the maximum insert rate a Waku node will operate within normal conditions.
+
+### Query test results (jmeter)
+
+In this case, we are comparing *Store* performance by means of Rest service.
+
+**Scenario**
+
+- node_a: one _nwaku_ node with *Store* and connected to *Postgres.*
+- node_b: one _nwaku_ node with *Store* and using *SQLite*.
+- Both *Postgres* and *SQLite* contain +1 million rows.
+- node_c: one _nwaku_ node with *REST* enabled and acting as a *Store client* for node_a.
+- node_d: one _nwaku_ node with *REST* enabled and acting as a *Store client* for node_b.
+- With _jmeter_, 10 users make *REST* *Store* requests concurrently to each of the “rest” nodes (node_c and node_d.)
+- All _nwaku_ nodes running statusteam/nim-waku:v0.19.0
+
+[This](https://github.com/waku-org/test-waku-query/blob/master/docker/jmeter/http_store_requests.jmx) is the _jmeter_ project used.
+
+
+
+*Results*
+
+With this, the *node_b* brings a higher throughput than the *node_a* and that indicates that the node that uses SQLite performs better. The following shows the measures taken by _jmeter_ with regard to the REST requests.
+
+
+
+### Query test results (only Store protocol)
+
+In this test suite, only the Store protocol is being analyzed, i.e. without REST. For that, a go-waku node is used, which acts as *Store* client. On the other hand, we have another go-waku app that publishes random *Relay* messages periodically. Therefore, this can be considered a more realistic approach.
+
+The following diagram shows the topology used:
+
+
+
+For that, the next apps were used:
+
+1. [Waku-publisher.](https://github.com/alrevuelta/waku-publisher/tree/9fb206c14a17dd37d20a9120022e86475ce0503f) This app can publish Relay messages with different numbers of clients
+2. [Waku-store-query-generator](https://github.com/Ivansete-status/waku-store-query-generator/tree/19e6455537b6d44199cf0c8558480af5c6788b0d). This app is based on the Waku-publisher but in this case, it can spawn concurrent go-waku Store clients.
+
+That topology is defined in [this](https://github.com/waku-org/test-waku-query/blob/7090cd125e739306357575730d0e54665c279670/docker/docker-compose-manual-binaries.yml) docker-compose file.
+
+Notice that the two `nwaku` nodes run the very same version, which is compiled locally.
+
+#### Comparing archive SQLite & Postgres performance in [nwaku-b6dd6899](https://github.com/waku-org/nwaku/tree/b6dd6899030ee628813dfd60ad1ad024345e7b41)
+
+The next results were obtained by running the docker-compose-manual-binaries.yml from [test-waku-query-c078075](https://github.com/waku-org/test-waku-query/tree/c07807597faa781ae6c8c32eefdf48ecac03a7ba) in the sandbox machine (metal-01.he-eu-hel1.misc.wakudev.status.im.)
+
+**Scenario 1**
+
+**Store rate:** 1 user generating 1 store-req/sec.
+
+**Relay rate:** 1 user generating 10msg/sec, 10KB each.
+
+In this case, we can see that the SQLite performance is better regarding the store requests.
+
+
+
+
+
+The following graph shows how the *SQLite* node has blocking periods whereas the *Postgres* always gives a steady rate.
+
+
+
+**Scenario 2**
+
+**Store rate:** 10 users generating 1 store-req/sec.
+
+**Relay rate:** 1 user generating 10msg/sec, 10KB each.
+
+In this case, is more evident that the *SQLite* performs better.
+
+
+
+
+
+**Scenario 3**
+
+**Store rate:** 25 users generating 1 store-req/sec.
+
+**Relay rate:** 1 user generating 10msg/sec, 10KB each.
+
+In this case, the performance is similar regarding the timings. The store rate is bigger in *SQLite* and *Postgres* keeps the same level as in scenario 2.
+
+
+
+
+
+#### Comparing archive SQLite & Postgres performance in [nwaku-b452ed8](https://github.com/waku-org/nwaku/tree/b452ed865466a33b7f5b87fa937a8471b28e466e)
+
+This nwaku commit is after a few **Postgres** optimizations were applied.
+
+The next results were obtained by running the docker-compose-manual-binaries.yml from [test-waku-query-c078075](https://github.com/waku-org/test-waku-query/tree/c07807597faa781ae6c8c32eefdf48ecac03a7ba) in the sandbox machine (metal-01.he-eu-hel1.misc.wakudev.status.im.)
+
+**Scenario 1**
+
+**Store rate** 1 user generating 1 store-req/sec. Notice that the current Store query used generates pagination which provokes more subsequent queries than the 1 req/sec that would be expected without pagination.
+
+**Relay rate:** 1 user generating 10msg/sec, 10KB each.
+
+
+
+
+
+It cannot be appreciated but the average *****Store***** time was 11ms.
+
+**Scenario 2**
+
+**Store rate:** 10 users generating 1 store-req/sec. Notice that the current Store query used generates pagination which provokes more subsequent queries than the 10 req/sec that would be expected without pagination.
+
+**Relay rate:** 1 user generating 10msg/sec, 10KB each.
+
+
+
+
+
+**Scenario 3**
+
+**Store rate:** 25 users generating 1 store-req/sec. Notice that the current Store query used generates pagination which provokes more subsequent queries than the 25 req/sec that would be expected without pagination.
+
+**Relay rate:** 1 user generating 10msg/sec, 10KB each.
+
+
+
+
+
+#### Conclusions
+
+After comparing both systems, *SQLite* performs much better than *Postgres* However, a benefit of using *Postgres* is that it performs asynchronous operations, and therefore doesn’t consume CPU time that would be better invested in *Relay* for example.
+
+Remember that _nwaku_ is single-threaded and *chronos* performs orchestration among a bunch of async tasks, and therefore it is not a good practice to block the whole _nwaku_ process in a query, as happens with *SQLite*
+
+After applying a few *Postgres* enhancements, it can be noticed that the use of concurrent *Store* queries doesn’t go below the 250ms barrier. The reason for that is that most of the time is being consumed in [this point](https://github.com/waku-org/nwaku/blob/6da1aeec5370bb1c116509e770178cca2662b69c/waku/common/databases/db_postgres/dbconn.nim#L124). The `libpqisBusy()` function indicates that the connection is still busy even the queries finished.
+
+Notice that we usually have a rate below 1100 req/minute in _status.prod_ fleet (checked November 7, 2023.)
+
+-----------------------------
+
+### Multiple nodes & one single database
+
+This study aims to look for possible issues when having only one single database while several Waku nodes insert or retrieve data from it.
+The following diagram shows the scenery used for such analysis.
+
+
+
+There are three nim-waku nodes that are connected to the same database and all of them are trying to write messages to the same _PostgreSQL_ instance. With that, it is very common to see errors like:
+```
+ERR 2023-11-27 13:18:07.575+00:00 failed to insert message topics="waku archive" tid=2921 file=archive.nim:111 err="error in runStmt: error in dbConnQueryPrepared calling waitQueryToFinish: error in query: ERROR: duplicate key value violates unique constraint \"messageindex\"\nDETAIL: Key (storedat, id, pubsubtopic)=(1701091087417938405, 479c95bbf74222417abf76c7f9c480a6790e454374dc4f59bbb15ca183ce1abd, /waku/2/default-waku/proto) already exists.\n
+```
+
+The `db-postgres-hammer` is aimed to stress the database from the `select` point of view. It performs `N` concurrent `select` queries with a certain rate.
+
+#### Results
+
+The following results were obtained by using the sandbox machine (metal-01.he-eu-hel1.misc.wakudev) and running nim-waku nodes from https://github.com/waku-org/nwaku/tree/b452ed865466a33b7f5b87fa937a8471b28e466e and using the `test-waku-query` project from https://github.com/waku-org/test-waku-query/tree/fef29cea182cc744c7940abc6c96d38a68739356
+
+The following shows the results
+
+1. Two `nwaku-postgres-additional` inserting messages plus 50 `db-postgres-hammer` making 10 `selects` per second.
+
+
+
+
+
+2. Five `nwaku-postgres-additional` inserting messages plus 50 `db-postgres-hammer` making 10 `selects` per second.
+
+
+
+In this case, the insert time gets more spread because the insert operations are shared amongst five more nodes. The _Store_ query time remains the same on average.
+
+3. Five `nwaku-postgres-additional` inserting messages plus 100 `db-postgres-hammer` making 10 `selects` per second.
+This case is similar to 2. but stressing more the database.
+
+
diff --git a/third-party/nwaku/docs/benchmarks/test-results-summary.md b/third-party/nwaku/docs/benchmarks/test-results-summary.md
new file mode 100644
index 0000000..b5786bf
--- /dev/null
+++ b/third-party/nwaku/docs/benchmarks/test-results-summary.md
@@ -0,0 +1,90 @@
+---
+title: Performance Benchmarks and Test Reports
+---
+
+
+## Introduction
+This page summarises key performance metrics for nwaku and provides links to detailed test reports.
+
+> ## TL;DR
+>
+> - Average Waku bandwidth usage: ~**10 KB/s** (minus discv5 Discovery) for 1KB message size and message injection rate of 1msg/s.
+Confirmed for topologies of up to 2000 Relay nodes.
+> - Average time for a message to propagate to 100% of nodes: **0.4s** for topologies of up to 2000 Relay nodes.
+> - Average per-node bandwidth usage of the discv5 protocol: **8 KB/s** for incoming traffic and **7.4 KB/s** for outgoing traffic,
+ in a network with 100 continuously online nodes.
+> - Future improvements: A messaging API is currently in development to streamline interactions with the Waku protocol suite.
+Once completed, it will enable benchmarking at the messaging API level, allowing applications to more easily compare their
+own performance results.
+
+
+## Insights
+
+### Relay Bandwidth Usage: nwaku v0.34.0
+The average per-node `libp2p` bandwidth usage in a 1000-node Relay network with 1KB messages at varying injection rates.
+
+
+| Message Injection Rate | Average libp2p incoming bandwidth (KB/s) | Average libp2p outgoing bandwidth (KB/s) |
+|------------------------|------------------------------------------|------------------------------------------|
+| 1 msg/s | ~10.1 | ~10.3 |
+| 1 msg/10s | ~1.8 | ~1.9 |
+
+### Message Propagation Latency: nwaku v0.34.0-rc1
+The message propagation latency is measured as the total time for a message to reach all nodes.
+We compare the latency in different network configurations for the following simulation parameters:
+- Total messages published: 600
+- Message size: 1KB
+- Message injection rate: 1msg/s
+
+The different network configurations tested are:
+- Relay Config: 1000 nodes with relay enabled
+- Mixed Config: 210 nodes, consisting of bootstrap nodes, filter clients and servers, lightpush clients and servers, store nodes
+- Non-persistent Relay Config: 500 persistent relay nodes, 10 store nodes and 100 non-persistent relay nodes
+
+Click on a specific config to see the detailed test report.
+
+| Config | Average Message Propagation Latency (s) | Max Message Propagation Latency (s)|
+|------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------|------------------------------------|
+| [Relay](https://www.notion.so/Waku-regression-testing-v0-34-1618f96fb65c803bb7bad6ecd6bafff9) (1000 nodes) | 0.05 | 1.6 |
+| [Mixed](https://www.notion.so/Mixed-environment-analysis-1688f96fb65c809eb235c59b97d6e15b) (210 nodes) | 0.0125 | 0.007 |
+| [Non-persistent Relay](https://www.notion.so/High-Churn-Relay-Store-Reliability-16c8f96fb65c8008bacaf5e86881160c) (510 nodes)| 0.0125 | 0.25 |
+
+### Discv5 Bandwidth Usage: nwaku v0.34.0
+The average bandwidth usage of discv5 for a network of 100 nodes and message injection rate of 0 or 1msg/s.
+The measurements are based on a stable network where all nodes have already connected to peers to form a healthy mesh.
+
+|Message size |Average discv5 incoming bandwidth (KB/s)|Average discv5 outgoing bandwidth (KB/s)|
+|-------------------- |----------------------------------------|----------------------------------------|
+| no message injection| 7.88 | 6.70 |
+| 1KB | 8.04 | 7.40 |
+| 10KB | 8.03 | 7.45 |
+
+## Testing
+### DST
+The VAC DST team performs regression testing on all new **nwaku** releases, comparing performance with previous versions.
+They simulate large Waku networks with a variety of network and protocol configurations that are representative of real-world usage.
+
+**Test Reports**: [DST Reports](https://www.notion.so/DST-Reports-1228f96fb65c80729cd1d98a7496fe6f)
+
+
+### QA
+The VAC QA team performs interoperability tests for **nwaku** and **go-waku** using the latest main branch builds.
+These tests run daily and verify protocol functionality by targeting specific features of each protocol.
+
+**Test Reports**: [QA Reports](https://discord.com/channels/1110799176264056863/1196933819614363678)
+
+### nwaku
+The **nwaku** team follows a structured release procedure for all release candidates.
+This involves deploying RCs to `status.staging` fleet for validation and performing sanity checks.
+
+**Release Process**: [nwaku Release Procedure](https://github.com/waku-org/nwaku/blob/master/.github/ISSUE_TEMPLATE/prepare_release.md)
+
+
+### Research
+The Waku Research team conducts a variety of benchmarking, performance testing, proof-of-concept validations and debugging efforts.
+They also maintain a Waku simulator designed for small-scale, single-purpose, on-demand testing.
+
+
+**Test Reports**: [Waku Research Reports](https://www.notion.so/Miscellaneous-2c02516248db4a28ba8cb2797a40d1bb)
+
+**Waku Simulator**: [Waku Simulator Book](https://waku-org.github.io/waku-simulator/)
diff --git a/third-party/nwaku/docs/contributors/README.md b/third-party/nwaku/docs/contributors/README.md
new file mode 100644
index 0000000..9f76cd7
--- /dev/null
+++ b/third-party/nwaku/docs/contributors/README.md
@@ -0,0 +1,11 @@
+# Contributors
+
+This folder contains documentation that is primarily useful for contributors. Some links and
+resources here might require privileged access.
+
+Example resources:
+
+- How to do releases
+- Viewing and modifying metrics dashboard
+- Continuous integration process
+- How to view Status cluster logs
diff --git a/third-party/nwaku/docs/contributors/cluster-logs.md b/third-party/nwaku/docs/contributors/cluster-logs.md
new file mode 100644
index 0000000..3e21c83
--- /dev/null
+++ b/third-party/nwaku/docs/contributors/cluster-logs.md
@@ -0,0 +1,11 @@
+# Cluster node logs
+
+These can be found in [Kibana](https://kibana.infra.status.im/goto/a3793b50-489d-11ed-a791-f14ad382fa11).
+
+Login with Github. For access issues, contact devops.
+
+Modify search field and time window as appropriate.
+
+Notice that there are two clusters, test and production. There is also a Waku v1 cluster.
+
+
diff --git a/third-party/nwaku/docs/contributors/continuous-integration.md b/third-party/nwaku/docs/contributors/continuous-integration.md
new file mode 100644
index 0000000..1c5fa56
--- /dev/null
+++ b/third-party/nwaku/docs/contributors/continuous-integration.md
@@ -0,0 +1,32 @@
+# Description
+
+This document describes the continuous integration setup for `nim-waku`.
+
+# Details
+
+The CI setup exists on the Status.im Jenkins instance:
+
+https://ci.infra.status.im/job/nim-waku/
+
+It currently consists of four jobs:
+
+* [manual](https://ci.infra.status.im/job/nim-waku/job/manual/) - For manually executing builds using parameters.
+* [deploy-waku-test](https://ci.infra.status.im/job/nim-waku/job/deploy-waku-test/) - Builds every new commit in `master` and deploys to `waku.test` fleet.
+* [deploy-waku-sandbox](https://ci.infra.status.im/job/nim-waku/job/deploy-waku-sandbox/) - Currently has no automatic trigger, and deploys to `waku.sandbox` fleet.
+
+# Configuration
+
+The main configuration file is [`Jenkinsfile.release`](../../ci/Jenkinsfile.release) in the `ci` folder.
+
+Key part is the definition of five `parameters`:
+
+* `MAKE_TARGET` - Which `Makefile` target is built.
+* `IMAGE_TAG` - Tag of the Docker image to push.
+* `IMAGE_NAME` - Name of the Docker image to push.
+* `NIMFLAGS` - Nim compilation parameters.
+* `GIT_REF` - Git reference to build from (branch, tag, commit...)
+
+The use of `?:` [Elvis operator](http://groovy-lang.org/operators.html#_elvis_operator) plays a key role in allowing parameters to be changed for each defined job in Jenkins without it being overridden by the `Jenkinsfile` defaults after every job run.
+```groovy
+defaultValue: params.IMAGE_TAG ?: 'deploy-waku-test',
+```
diff --git a/third-party/nwaku/docs/contributors/git-submodules.md b/third-party/nwaku/docs/contributors/git-submodules.md
new file mode 100644
index 0000000..52336e2
--- /dev/null
+++ b/third-party/nwaku/docs/contributors/git-submodules.md
@@ -0,0 +1,10 @@
+# Submodules
+
+We use Git submodules in the `vendor` directory to track internal Nim
+dependencies. We want to update submodules all at once to avoid issues.
+
+```
+git submodule foreach --recursive git submodule update --init
+git submodule update --remote
+```
+
diff --git a/third-party/nwaku/docs/contributors/nph.md b/third-party/nwaku/docs/contributors/nph.md
new file mode 100644
index 0000000..91f4149
--- /dev/null
+++ b/third-party/nwaku/docs/contributors/nph.md
@@ -0,0 +1,34 @@
+
+## nph - An opinionated Nim formatter
+This prettifier tool is used to format the nwaku code base.
+
+### VSCode Extension
+https://marketplace.visualstudio.com/items?itemName=arnetheduck.vscode-nph
+
+### GitHub
+https://github.com/arnetheduck/nph
+
+Make sure you use a binary from the following release:
+https://github.com/arnetheduck/nph/releases/tag/v0.5.1
+
+```bash
+$ nph --version
+v0.5.1-0-gde5cd48
+```
+
+### Installation and configuration
+1. Ask the [nwaku team](https://discord.com/channels/1110799176264056863/1111541184490393691) about the required `nph` version.
+2. Download the desired release from _GitHub_ and place the binary in the PATH env var.
+3. Add the following content into `~/.config/Code/User/settings.json`:
+
+```
+{
+ "[nim]": {
+ "editor.formatOnSave": true,
+ "editor.defaultFormatter": "arnetheduck.vscode-nph"
+ },
+}
+```
+
+With that, every time a Nim file is saved, it will be formatted automatically.
+
diff --git a/third-party/nwaku/docs/contributors/release-process.md b/third-party/nwaku/docs/contributors/release-process.md
new file mode 100644
index 0000000..c0fb12d
--- /dev/null
+++ b/third-party/nwaku/docs/contributors/release-process.md
@@ -0,0 +1,119 @@
+# Release Process
+
+How to do releases.
+
+For more context, see https://trunkbaseddevelopment.com/branch-for-release/
+
+## How to do releases
+
+### Before release
+
+Ensure all items in this list are ticked:
+- [ ] All issues under the corresponding release [milestone](https://github.com/waku-org/nwaku/milestones) has been closed or, after consultation, deferred to a next release.
+- [ ] All submodules are up to date.
+ > **IMPORTANT:** Updating submodules requires a PR (and very often several "fixes" to maintain compatibility with the changes in submodules). That PR process must be done and merged a couple of days before the release.
+ > In case the submodules update has a low effort and/or risk for the release, follow the ["Update submodules"](./git-submodules.md) instructions.
+ > If the effort or risk is too high, consider postponing the submodules upgrade for the subsequent release or delaying the current release until the submodules updates are included in the release candidate.
+- [ ] The [js-waku CI tests](https://github.com/waku-org/js-waku/actions/workflows/ci.yml) pass against the release candidate (i.e. nwaku latest `master`).
+ > **NOTE:** This serves as a basic regression test against typical clients of nwaku.
+ > The specific job that needs to pass is named `node_with_nwaku_master`.
+
+### Performing the release
+
+1. Checkout a release branch from master
+
+ ```
+ git checkout -b release/v0.1.0
+ ```
+
+1. Update `CHANGELOG.md` and ensure it is up to date. Use the helper Make target to get PR based release-notes/changelog update.
+
+ ```
+ make release-notes
+ ```
+
+1. Create a release-candidate tag with the same name as release and `-rc.N` suffix a few days before the official release and push it
+
+ ```
+ git tag -as v0.1.0-rc.0 -m "Initial release."
+ git push origin v0.1.0-rc.0
+ ```
+
+ This will trigger a [workflow](../../.github/workflows/pre-release.yml) which will build RC artifacts and create and publish a Github release
+
+1. Open a PR from the release branch for others to review the included changes and the release-notes
+
+1. In case additional changes are needed, create a new RC tag
+
+ Make sure the new tag is associated
+ with CHANGELOG update.
+
+ ```
+ # Make changes, rebase and create new tag
+ # Squash to one commit and make a nice commit message
+ git rebase -i origin/master
+ git tag -as v0.1.0-rc.1 -m "Initial release."
+ git push origin v0.1.0-rc.1
+ ```
+
+1. Validate the release. For the release validation process, please refer to the following [guide](https://www.notion.so/Release-Process-61234f335b904cd0943a5033ed8f42b4#47af557e7f9744c68fdbe5240bf93ca9)
+
+1. Once the release-candidate has been validated, create a final release tag and push it.
+We also need to merge release branch back to master as a final step.
+
+ ```
+ git checkout release/v0.1.0
+ git tag -as v0.1.0 -m "Initial release."
+ git push origin v0.1.0
+ git switch master
+ git pull
+ git merge release/v0.1.0
+ ```
+
+1. Create a [Github release](https://github.com/waku-org/nwaku/releases) from the release tag.
+
+ * Add binaries produced by the ["Upload Release Asset"](https://github.com/waku-org/nwaku/actions/workflows/release-assets.yml) workflow. Where possible, test the binaries before uploading to the release.
+
+### After the release
+
+1. Announce the release on Twitter, Discord and other channels.
+2. Deploy the release image to [Dockerhub](https://hub.docker.com/r/wakuorg/nwaku) by triggering [the manual Jenkins deployment job](https://ci.infra.status.im/job/nim-waku/job/docker-manual/).
+ > Ensure the following build parameters are set:
+ > - `MAKE_TARGET`: `wakunode2`
+ > - `IMAGE_TAG`: the release tag (e.g. `v0.16.0`)
+ > - `IMAGE_NAME`: `wakuorg/nwaku`
+ > - `NIMFLAGS`: `--colors:off -d:disableMarchNative -d:chronicles_colors:none -d:postgres`
+ > - `GIT_REF` the release tag (e.g. `v0.16.0`)
+3. Update the default nwaku image in [nwaku-compose](https://github.com/waku-org/nwaku-compose/blob/master/docker-compose.yml)
+4. Deploy the release to appropriate fleets:
+ - Inform clients
+ > **NOTE:** known clients are currently using some version of js-waku, go-waku, nwaku or waku-rs.
+ > Clients are reachable via the corresponding channels on the Vac Discord server.
+ > It should be enough to inform clients on the `#nwaku` and `#announce` channels on Discord.
+ > Informal conversations with specific repo maintainers are often part of this process.
+ - Check if nwaku configuration parameters changed. If so [update fleet configuration](https://www.notion.so/Fleet-Ownership-7532aad8896d46599abac3c274189741?pvs=4#d2d2f0fe4b3c429fbd860a1d64f89a64) in [infra-nim-waku](https://github.com/status-im/infra-nim-waku)
+ - Deploy release to the `waku.sandbox` fleet from [Jenkins](https://ci.infra.status.im/job/nim-waku/job/deploy-waku-sandbox/).
+ - Ensure that nodes successfully start up and monitor health using [Grafana](https://grafana.infra.status.im/d/qrp_ZCTGz/nim-waku-v2?orgId=1) and [Kibana](https://kibana.infra.status.im/goto/a7728e70-eb26-11ec-81d1-210eb3022c76).
+ - If necessary, revert by deploying the previous release. Download logs and open a bug report issue.
+5. Submit a PR to merge the release branch back to `master`. Make sure you use the option `Merge pull request (Create a merge commit)` to perform such merge.
+
+### Performing a patch release
+
+1. Cherry-pick the relevant commits from master to the release branch
+
+ ```
+ git cherry-pick
+ ```
+
+2. Create a release-candidate tag with the same name as release and `-rc.N` suffix
+
+3. Update `CHANGELOG.md`. From the release branch, use the helper Make target after having cherry-picked the commits.
+
+ ```
+ make release-notes
+ ```
+ Create a new branch and raise a PR with the changelog updates to master.
+
+4. Once the release-candidate has been validated and changelog PR got merged, cherry-pick the changelog update from master to the release branch. Create a final release tag and push it.
+
+5. Create a [Github release](https://github.com/waku-org/nwaku/releases) from the release tag and follow the same post-release process as usual.
diff --git a/third-party/nwaku/docs/contributors/waku-fleets.md b/third-party/nwaku/docs/contributors/waku-fleets.md
new file mode 100644
index 0000000..659d855
--- /dev/null
+++ b/third-party/nwaku/docs/contributors/waku-fleets.md
@@ -0,0 +1,107 @@
+# Waku fleet: management & monitoring
+
+## Background
+
+Status currently maintains two fleets for `nwaku` nodes,
+the `waku.test` fleet and the `waku.sandbox` (sandbox) fleet.
+They'll be referred to as `test` and `sandbox` in this document.
+Status fleet nodes and addresses can be viewed [here](https://fleets.status.im/).
+
+### Fleet overview
+
+At the time of writing this, each fleet consists of three waku nodes,
+with a [websockify](https://github.com/novnc/websockify) WebSocket-to-TCP bridge for each node.
+Waku peers can choose to connect either directly to a node's TCP endpoint
+or the bridged WebSocket depending on their own supported transports.
+The `sandbox` fleet also has a deployed [`chat2bridge`](https://github.com/waku-org/nwaku/blob/master/docs/tutorial/chat2.md#bridge-messages-between-chat2-and-matterbridge),
+which serves as a bridge between the [Waku toy-chat](https://rfc.vac.dev/spec/22/) and Matterbridge.
+The `chat2bridge` is currently deployed to the `node-01.do-ams3` datacentre
+and configured to bridge toy-chat messages to the `#waku channel` on the Vac Discord Server.
+
+### Fleet deployment rationale
+
+The `test` fleet is automatically updated after every commit to the `nwaku` repository `master` branch and is therefore the most up to date representation of Waku development.
+It is suitable for testing new features before they're rolled out to the (more) stable `sandbox` fleet.
+
+In general only the latest release of `nwaku` is deployed to the `sandbox` fleet.
+It requires manual updating and should therefore be more stable than `test`.
+See the [section on Jenkins](#jenkins-for-deployment) below for more on the deployment process.
+
+### Related repos
+
+The [`infra-docs` repo](https://github.com/status-im/infra-docs) contains the most comprehensive overview of Status infrastructure.
+This is a private repository.
+Feel free to contact someone in the team to request access.
+
+The [`infra-nim-waku` repo](https://github.com/status-im/infra-nim-waku) contains the infrastructure definitions for Waku nodes implemented in Nim.
+
+## Monitoring and management
+
+The rest of this document highlights some infra services of specific interest to Waku fleet monitoring and management:
+
+1. [Consul](https://consul.infra.status.im/ui/do-ams3/services?filter=nim-waku) to view the health status of Waku nodes.
+2. [Kibana](https://kibana.infra.status.im/app/discover#/) to view and filter logs.
+3. [Grafana](https://grafana.infra.status.im/d/qrp_ZCTGz/nim-waku-v2) to view and filter metrics.
+4. [Jenkins](https://ci.infra.status.im/job/nim-waku/) to configure and deploy new builds to the fleets.
+
+### 1. [Consul](https://consul.infra.status.im/ui/do-ams3/services?filter=nim-waku) for health checks
+
+Consul provides a useful high-level view of the health of the `nwaku` fleets.
+It aggregates the result of various monitoring checks
+and shows the health status for the node itself, the RPC API, exposed WebSocket and metrics.
+The datacentre can be changed in the upper left-hand corner.
+
+### 2. [Kibana](https://kibana.infra.status.im/app/discover#/) for logs
+
+Kibana is a powerful visualisation tool for Elasticsearch data.
+For Waku fleets it can be used to retrieve, filter and view the logs for all deployed services.
+For example, to view the latest logs for `sandbox`,
+Kibana can be opened in "Discover" mode with an [active filter for `fleet: waku.sandbox`](https://kibana.infra.status.im/goto/c0434f60-ca82-11ee-aaa4-85391103106b).
+
+### 3. [Grafana](https://grafana.infra.status.im/d/qrp_ZCTGz/nim-waku-v2?orgId=1&refresh=5m) for metrics
+
+The `Nim-Waku` Grafana dashboard displays live and historical metrics for Waku nodes.
+The default view includes metrics from both fleets,
+though it's possible to filter by `Hostname`, `Fleet name` or `Data Center`.
+The time range can also be configured -
+by default the latest metrics will be shown.
+
+The dashboard itself includes an _"At a glance"_ summary
+with an overview of the latest connected peers, total messages, CPU usage, reported errors, etc.
+The _"General"_ collection contains a more in-depth look at node, libp2p and performance-related metrics.
+This is followed by separate panel collections showing _per-protocol_ metrics.
+
+A copy of the `Nim-Waku` fleets dashboard is maintained in the [`nwaku` repo](https://github.com/waku-org/nwaku/blob/master/metrics/waku-fleet-dashboard.json).
+From time to time certain Prometheus queries may fail,
+often when the underlying metrics are renamed.
+Please report any broken panels via our Discord channels or by [creating an issue in `nwaku`](https://github.com/waku-org/nwaku/issues/new).
+
+### 4. [Jenkins](https://ci.status.im/job/nim-waku/) for deployment
+
+The [`nim-waku` jobs](https://ci.infra.status.im/job/nim-waku/) on Jenkins are configured to deploy `nwaku` builds to the fleets.
+1. [`deploy-waku-test`](https://ci.infra.status.im/job/nim-waku/job/deploy-waku-test/) is triggered automatically after every commit to the `nwaku` `master` branch.
+2. [`deploy-waku-sandbox`](https://ci.infra.status.im/job/nim-waku/job/deploy-waku-sandbox/) must be triggered manually. Usually this job is only built after a tagged release in `nwaku`.
+
+Each job can be manually triggered using the _"Build with Parameters"_ option.
+Options under _"Configure"_ include the build triggers, build target and branches to build.
+These should only be changed with care.
+
+See [Continuous Integration docs](https://github.com/waku-org/nwaku/blob/master/docs/contributors/continuous-integration.md) for more.
+
+## Quick links
+
+ 1. [`chat2bridge`](https://github.com/waku-org/nwaku/blob/master/docs/tutorial/chat2.md#bridge-messages-between-chat2-and-matterbridge)
+ 2. [Consul for do-ams3](https://consul.infra.status.im/ui/do-ams3/services?filter=nim-waku)
+ 3. [Consul for ac-cn-hongkong-c](https://consul.infra.status.im/ui/ac-cn-hongkong-c/services?filter=nim-waku)
+ 4. [Consul for gc-us-central1-a](https://consul.infra.status.im/ui/gc-us-central1-a/services?filter=nim-waku)
+ 5. [Grafana Nim-Waku dashboard](https://grafana.infra.status.im/d/qrp_ZCTGz/nim-waku-v2?orgId=1&refresh=5m)
+ 6. [`infra-docs` repo](https://github.com/status-im/infra-docs)
+ 7. [`infra-waku` repo](https://github.com/status-im/infra-waku)
+ 8. [Jenkins jobs for `nim-waku`](https://ci.infra.status.im/job/nim-waku/)
+ 9. [Jenkins deploy-waku-sandbox manual trigger](https://ci.infra.status.im/job/nim-waku/job/deploy-waku-sandbox/build)
+ 10. [Jenkins deploy-waku-test manual trigger](https://ci.infra.status.im/job/nim-waku/job/deploy-waku-test/build)
+ 11. [Kibana logs for `sandbox`](https://kibana.infra.status.im/goto/c0434f60-ca82-11ee-aaa4-85391103106b)
+ 12. [Kibana logs for `test`](https://kibana.infra.status.im/goto/7cd22f20-ca83-11ee-aaa4-85391103106b)
+ 13. [Status fleets](https://fleets.status.im/)
+ 14. [Status fleets - Table](https://fleets.waku.org)
+ 15. [Websockify](https://github.com/novnc/websockify)
diff --git a/third-party/nwaku/docs/faq.md b/third-party/nwaku/docs/faq.md
new file mode 100644
index 0000000..40a189c
--- /dev/null
+++ b/third-party/nwaku/docs/faq.md
@@ -0,0 +1,34 @@
+# FAQ
+
+## How do I see what address a node is listening for?
+
+Grep for "Listening on". It should be printed at INFO level at the beginning. E.g. from Kibana:
+
+`Oct 7, 2020 @ 23:17:00.383INF 2020-10-07 23:17:00.375+00:00 Listening on topics="wakunode" tid=1 file=wakunode2.nim:140 full=/ip4/0.0.0.0/tcp/60000/p2p/16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp`
+
+## How do I find out node addresses at the test cluster?
+
+The easiest way is to use `jq` and query the fleets registry that Status operates:
+
+```
+curl -s https://fleets.status.im | jq '.fleets["waku.test"]'
+
+# Output
+{
+ "tcp/p2p/waku": {
+ "node-01.do-ams3.waku.test": "/dns4/node-01.do-ams3.waku.test.status.im/tcp/30303/p2p/16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W",
+ "node-01.gc-us-central1-a.waku.test": "/dns4/node-01.gc-us-central1-a.waku.test.status.im/tcp/30303/p2p/16Uiu2HAmDCp8XJ9z1ev18zuv8NHekAsjNyezAvmMfFEJkiharitG",
+ "node-01.ac-cn-hongkong-c.waku.test": "/dns4/node-01.ac-cn-hongkong-c.waku.test.status.im/tcp/30303/p2p/16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp"
+ },
+ "enr/p2p/waku": {
+ "node-01.do-ams3.waku.test": "enr:-QESuEC1p_s3xJzAC_XlOuuNrhVUETmfhbm1wxRGis0f7DlqGSw2FM-p2Ugl_r25UHQJ3f1rIRrpzxJXSMaJe4yk1XFSAYJpZIJ2NIJpcISygI2rim11bHRpYWRkcnO4XAArNiZub2RlLTAxLmRvLWFtczMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAZ2XwAtNiZub2RlLTAxLmRvLWFtczMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQJATXRSRSUyTw_QLB6H_U3oziVQgNRgrXpK7wp2AMyNxYN0Y3CCdl-DdWRwgiMohXdha3UyDw",
+ "node-01.gc-us-central1-a.waku.test": "enr:-QEkuECnZ3IbVAgkOzv-QLnKC4dRKAPRY80m1-R7G8jZ7yfT3ipEfBrhKN7ARcQgQ-vg-h40AQzyvAkPYlHPaFKk6u9uAYJpZIJ2NIJpcIQiEAFDim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAZ2XwA2Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQMIJwesBVgUiBCi8yiXGx7RWylBQkYm1U9dvEy-neLG2YN0Y3CCdl-DdWRwgiMohXdha3UyDw",
+ "node-01.ac-cn-hongkong-c.waku.test": "enr:-QEkuEDzQyIAhs-CgBHIrJqtBv3EY1uP1Psrc-y8yJKsmxW7dh3DNcq2ergMUWSFVcJNlfcgBeVsFPkgd_QopRIiCV2pAYJpZIJ2NIJpcIQI2ttrim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmFjLWNuLWhvbmdrb25nLWMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAZ2XwA2Ni9ub2RlLTAxLmFjLWNuLWhvbmdrb25nLWMud2FrdS50ZXN0LnN0YXR1c2ltLm5ldAYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQJIN4qwz3v4r2Q8Bv8zZD0eqBcKw6bdLvdkV7-JLjqIj4N0Y3CCdl-DdWRwgiMohXdha3UyDw"
+ },
+ "wss/p2p/waku": {
+ "node-01.do-ams3.waku.test": "/dns4/node-01.do-ams3.waku.test.status.im/tcp/8000/wss/p2p/16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W",
+ "node-01.gc-us-central1-a.waku.test": "/dns4/node-01.gc-us-central1-a.waku.test.status.im/tcp/8000/wss/p2p/16Uiu2HAmDCp8XJ9z1ev18zuv8NHekAsjNyezAvmMfFEJkiharitG",
+ "node-01.ac-cn-hongkong-c.waku.test": "/dns4/node-01.ac-cn-hongkong-c.waku.test.status.im/tcp/8000/wss/p2p/16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp"
+ }
+}
+```
diff --git a/third-party/nwaku/docs/operators/README.md b/third-party/nwaku/docs/operators/README.md
new file mode 100644
index 0000000..5850977
--- /dev/null
+++ b/third-party/nwaku/docs/operators/README.md
@@ -0,0 +1,35 @@
+# The nwaku guide for operators
+
+*If you're eager to get started, check out our [quickstart guide](./quickstart.md) for typical configurations or [step-by-step overview](./overview.md) for newcomers.*
+
+Nwaku is a client implementation in Nim of the [Waku v2 family of protocols](https://rfc.vac.dev/spec/10/) for peer-to-peer communication.
+The protocols are designed to be secure, privacy-preserving, censorship-resistant and able to run in resource restricted environments.
+Moreover, we've taken a modular approach so that node operators can choose which protocols they want to support
+based on their own motivations and availability of resources.
+We call this concept ["adaptive nodes"](https://rfc.vac.dev/spec/30/),
+implying that a Waku v2 network can consist of heterogeneous nodes contributing at different levels to the network.
+
+Nwaku (formerly `nim-waku`) aims to be a lightweight and robust Waku v2 client.
+It serves as the reference implementation for researchers,
+who extend the client in parallel to spec development.
+As such, it is first in line to support innovative and new Waku v2 protocols,
+but configurable enough to serve the adaptive needs of various operators.
+We are also developing a set of operator-focused tools to monitor and maintain a running nwaku node.
+
+This guide provides step-by-step tutorials covering how to build and configure your own nwaku node,
+connect to an existing Waku v2 network
+and use existing tools for monitoring and maintaining a running node.
+
+## Helpful resources
+
+
+
+## Getting in touch or reporting an issue
+
+For an inquiry, or if you would like to propose new features, feel free to [open a general issue](https://github.com/waku-org/nwaku/issues/new/).
+
+For bug reports, please [tag your issue with the `bug` label](https://github.com/waku-org/nwaku/issues/new/).
+
+If you believe the reported issue requires critical attention, please [use the `critical` label](https://github.com/waku-org/nwaku/issues/new?labels=critical,bug) to assist with triaging.
+
+To get help, or participate in the conversation, join the [Vac Discord](https://discord.gg/KNj3ctuZvZ) server.
diff --git a/third-party/nwaku/docs/operators/docker-quickstart.md b/third-party/nwaku/docs/operators/docker-quickstart.md
new file mode 100644
index 0000000..0907772
--- /dev/null
+++ b/third-party/nwaku/docs/operators/docker-quickstart.md
@@ -0,0 +1,79 @@
+# Quickstart: running nwaku in a Docker container
+
+This guide explains how to run a nwaku node in a Docker container.
+
+## Prerequisites
+
+Make sure you have Docker installed.
+Installation instructions for different platforms can be found in the [Docker docs](https://docs.docker.com/engine/install/).
+
+For example, to use Docker's convenience script for installation:
+
+```bash
+curl -fsSL https://get.docker.com -o get-docker.sh
+sudo sh get-docker.sh
+```
+
+## Step 1: Get Docker image
+
+Nwaku Docker images are published to the Docker Hub public registry under [`wakuorg/nwaku`](https://hub.docker.com/r/wakuorg/nwaku).
+For specific releases the published images are tagged with the release version, e.g. [`wakuorg/nwaku:v0.20.0`](https://hub.docker.com/layers/wakuorg/nwaku/v0.20.0/images/sha256-9976ac2dc536fae49b21f7b77618aa6f0efb59c694e7b3181e54c08be0c4f089?context=explore).
+Images are also published for each commit to the `master` branch in the [nwaku repo](https://github.com/status-im/nwaku/commits/master)
+and tagged with the corresponding commit hash.
+See [`wakuorg/nwaku`](https://hub.docker.com/r/wakuorg/nwaku/tags) on Docker Hub for a full list of available tags.
+
+To pull the image of your choice, use
+
+```bash
+docker pull wakuorg/nwaku:v0.20.0 # or, whichever tag you prefer in the format wakuorg/nwaku:[tag]
+```
+
+You can also build the Docker image locally using
+
+```bash
+git clone --recurse-submodules https://github.com/waku-org/nwaku
+cd nwaku
+docker build -t wakuorg/nwaku:latest .
+```
+
+## Step 2: Run
+
+To run nwaku in a new Docker container,
+use the following command:
+
+```bash
+docker run [OPTIONS] IMAGE [ARG...]
+```
+
+where `OPTIONS` are your selected Docker options,
+`IMAGE` the image and tag you pulled from the registry or built in Step 1
+and `ARG...` the list of nwaku arguments for your [chosen nwaku configuration](./how-to/configure.md).
+
+For Docker options we recommend explicit port mappings (`-p`) at least
+for your exposed libp2p listening ports
+and any discovery ports (e.g. the Waku discv5 port) that must be reachable from outside the host.
+
+As an example, consider the following command to run nwaku in a Docker container with the most typical configuration:
+
+```bash
+docker run -i -t -p 60000:60000 -p 9000:9000/udp wakuorg/nwaku:v0.20.0 \
+ --dns-discovery:true \
+ --dns-discovery-url:enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im \
+ --discv5-discovery \
+ --nat:extip:[yourpublicip] # or, if you are behind a nat: --nat=any
+```
+
+This runs nwaku in a new container from the `wakuorg/nwaku:v0.20.0` image,
+connects to `wakuv.sandbox` as bootstrap fleet and
+enables [Waku Discovery v5](https://rfc.vac.dev/spec/33/) for ambient peer discovery,
+while mapping the default libp2p listening port (`60000`)
+and default discv5 UDP port (`9000`) to the host.
+
+> **Tip:** The `docker run` command will pull the specified image from Docker Hub if it's not yet available locally,
+so it's possible to skip Step 1 and pull the image from your configured registry automatically when running.
+
+If you've used the `-i` and `-t` Docker options when running the new container,
+the `run` command would have allocated an interactive terminal
+where you'll see the `stdout` logs from the running nwaku process.
+To detach gracefully from the running container,
+use `Ctrl-P` followed by `Ctrl-Q`.
diff --git a/third-party/nwaku/docs/operators/droplet-quickstart.md b/third-party/nwaku/docs/operators/droplet-quickstart.md
new file mode 100644
index 0000000..070cf6d
--- /dev/null
+++ b/third-party/nwaku/docs/operators/droplet-quickstart.md
@@ -0,0 +1,305 @@
+# Quickstart for running nwaku on a DigitalOcean Droplet
+
+This guide explains how to run a nwaku node on a
+DigitalOcean Droplet. We enable the following protocols -
+
+1. Relay
+2. Store
+3. DNS Discovery
+4. Discv5
+
+A Droplet is a simple virtual machine that runs in DigitalOcean's datacenters.
+
+Note that Droplets do cost money, the size described in the guide costs approximately $12 a month.
+
+The guide makes heavy use of the `doctl` cli to make it as UI agnostic as possible.
+There are similar steps to accomplish the same through DigitalOcean's cloud console, accessible [here](https://cloud.digitalocean.com/)
+
+## Prerequisites
+
+1. A DigitalOcean account. Upon signing up, you have $100 worth of credits to use.
+
+
+
+## 1. Get the `doctl` binary
+
+Follow this [guide](https://docs.digitalocean.com/reference/doctl/how-to/install/) to install,
+and configure the `doctl` cli, which will help with setting up the Droplet.
+
+> Note: It is not required to set up the droplet that is mentioned in the `doctl` cli guide
+
+## 2. Set up SSH credentials
+
+Run the following command -
+```bash
+export DROPLET_SSH_KEY_PATH=~/.ssh/id_nwaku_droplet
+ssh-keygen -f $DROPLET_SSH_KEY_PATH
+```
+
+Press `enter` twice, i.e do NOT set a passphrase.
+
+Run the following command -
+```bash
+export DROPLET_SSH_PUBLIC_KEY=$(cat "$DROPLET_SSH_KEY_PATH".pub)
+```
+
+*Alternatively*, if you would like to supply your own credentials, make sure that the public key is in the `DROPLET_SSH_PUBLIC_KEY` env variable.
+
+
+Lastly, add the ssh key to your DigitalOcean account -
+```bash
+doctl compute ssh-key create nwaku-key --public-key="$DROPLET_SSH_PUBLIC_KEY"
+```
+
+## 3. Select the region closest to you
+
+Run the following command to get the list of available
+regions -
+
+```bash
+doctl compute region list | grep true
+```
+
+You should get an output similar to this -
+
+```bash
+nyc1 New York 1 true
+sgp1 Singapore 1 true
+lon1 London 1 true
+nyc3 New York 3 true
+ams3 Amsterdam 3 true
+fra1 Frankfurt 1 true
+tor1 Toronto 1 true
+blr1 Bangalore 1 true
+sfo3 San Francisco 3 true
+```
+Choose the region closest to you, and run the following command -
+
+```bash
+export DROPLET_REGION=
+```
+
+For example, if you live in NYC -
+```bash
+export DROPLET_REGION=nyc1
+```
+
+Note that it is *optional* to choose the datacenter closest to you. This is merely done for operational efficiency.
+
+## 4. Select the OS distribution
+
+Run the following command to get the list of distributions -
+
+```bash
+doctl compute image list-distribution
+```
+
+You should get an output similar to this -
+
+```bash
+ID Name Type Distribution Slug Public Min Disk
+78547182 1.5.8 x64 snapshot RancherOS rancheros true 15
+106433672 7 x64 snapshot CentOS centos-7-x64 true 9
+106434098 9 Stream x64 snapshot CentOS centos-stream-9-x64 true 10
+106434191 8 Stream x64 snapshot CentOS centos-stream-8-x64 true 10
+...
+```
+
+Choose the distribution you are most comfortable with, and then run the following command
+
+```bash
+export DROPLET_IMAGE=
+```
+
+For example, if you chose Debian 11 x64 -
+
+```bash
+export DROPLET_IMAGE=debian-11-x64
+```
+
+## 5. Select the size of the Droplet
+
+Run the following command to get the list of Droplet sizes for the previously selected region -
+
+```bash
+doctl compute size list
+```
+
+You should get an output similar to this -
+```bash
+Slug Description Memory VCPUs Disk Price Monthly Price Hourly
+s-1vcpu-512mb-10gb Basic 512 1 10 4.00 0.005950
+s-1vcpu-1gb Basic 1024 1 25 6.00 0.008930
+s-1vcpu-1gb-amd Basic AMD 1024 1 25 7.00 0.010420
+s-1vcpu-1gb-intel Basic Intel 1024 1 25 7.00 0.010420
+s-1vcpu-2gb Basic 2048 1 50 12.00 0.017860
+s-1vcpu-2gb-amd Basic AMD 2048 1 50 14.00 0.020830
+s-1vcpu-2gb-intel Basic Intel 2048 1 50 14.00 0.020830
+s-2vcpu-2gb Legacy Basic 2048 2 60 18.00 0.026790
+...
+```
+
+> Note: To compile the nwaku binary, a minimum of 2GB of RAM is required. You may choose a smaller Droplet, however, you would have to supply the binary in an alternate manner, i.e via the official release on Github, or compiling it on another machine and copying it over. Currently, we only supply binaries for macOS and Ubuntu.
+
+Choose the Droplet size that you are most comfortable with, and then run the following command -
+
+```bash
+export DROPLET_SIZE=
+```
+
+For example, `s-1vcpu-2gb` is more than capable to handle the protocols we mentioned above -
+
+```bash
+export DROPLET_SIZE=s-1vcpu-2gb
+```
+
+## 6. Create the Droplet
+
+Run the following command to create the droplet -
+
+> Note: Droplet names must be valid hostnames, i.e they must only contain alphanumeric characters and hyphens (-)
+
+```bash
+export DROPLET_NAME=
+export DROPLET_ID=$(doctl compute droplet create --region=$DROPLET_REGION --image=$DROPLET_IMAGE --size=$DROPLET_SIZE --enable-monitoring --format=ID --wait $DROPLET_NAME | sed -n '2 p')
+```
+
+For example, to create a droplet named `nwaku` -
+
+```bash
+export DROPLET_NAME=nwaku
+export DROPLET_ID=$(doctl compute droplet create --region=$DROPLET_REGION --image=$DROPLET_IMAGE --size=$DROPLET_SIZE --enable-monitoring --format=ID --wait $DROPLET_NAME | sed -n '2 p')
+```
+
+## 7. Create a Domain and attach it to the droplet (OPTIONAL)
+
+Follow this [guide](https://docs.digitalocean.com/products/networking/dns/how-to/add-domains/) to create a domain, and add it to the droplet appropriately.
+
+## 8. SSH into the Droplet
+
+You can get the following details in the email that DigitalOcean sends upon successful creation of the Droplet -
+
+1. username
+2. password
+3. public ipv4 address
+
+Since the public key we previously generated was automatically added to the authorized_keys list, we can run the following command to ssh into the Droplet -
+
+```bash
+export DROPLET_USERNAME=
+export DROPLET_IP=
+ssh -i $DROPLET_SSH_KEY_PATH $DROPLET_USERNAME@$DROPLET_IP
+```
+
+For example, if the username was `root`, and the ipv4 address was `0.0.0.0`,
+
+```bash
+export USERNAME=root
+export IP=0.0.0.0
+ssh -i $DROPLET_SSH_KEY_PATH $DROPLET_USERNAME@$DROPLET_IP
+```
+
+Enter the password received in the email.
+
+## 9. Build nwaku
+
+To build `nwaku`, follow this [guide](./how-to/build.md)
+
+OR
+
+To fetch the latest release from Github, navigate to https://github.com/status-im/nwaku/releases and download the latest tarball for your distribution.
+
+This [guide](https://www.itprotoday.com/development-techniques-and-management/how-install-targz-file-ubuntu-linux) describes how to install a tarball for your distribution.
+
+OR
+
+Run the following script to copy over the wakunode2 binary (from the host machine) -
+
+```bash
+scp -i $DROPLET_SSH_KEY_PATH ./build/wakunode2 $DROPLET_USERNAME@$DROPLET_IP:~/wakunode2
+```
+
+## 10. Set up a terminal multiplexer of choice
+
+You may decide to use either `screen` or `tmux` to be able to reattach to the process
+after closing the ssh connection.
+
+Installation instructions for -
+1. [screen](https://linuxhint.com/screen-linux/)
+2. [tmux](https://linuxhint.com/install-tmux-ubuntu/)
+
+## 10. Run nwaku
+
+First, start the `screen` or `tmux` session by following the instructions of the terminal multiplexer chosen previously -
+1. [screen](https://linuxize.com/post/how-to-use-linux-screen/#starting-linux-screen)
+2. [tmux](https://linuxize.com/post/getting-started-with-tmux/#starting-your-first-tmux-session)
+
+Run the following command to run `nwaku` -
+
+*Note the path to the wakunode2 binary*
+
+a. Add the parent directory of the wakunode2 binary to your environment:
+
+ If you built it locally and copied it via scp -
+
+ ```bash
+ export WAKUNODE_DIR="$pwd"
+ ```
+
+ OR
+
+ If you compiled it on the Droplet -
+
+ ```bash
+ export WAKUNODE_DIR="$pwd"/build
+ ```
+
+b. Choose the fleet you wish to connect your node to:
+ - waku sandbox: enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im
+ - waku test: enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im
+
+ ```bash
+ export WAKU_FLEET=
+ ```
+
+
+c. Run `nwaku`:
+
+ If you set up a domain previously -
+
+ ```bash
+ export DOMAIN_NAME=
+ $WAKUNODE_DIR/wakunode2 \
+ --store:true \
+ --persist-messages \
+ --dns-discovery \
+ --dns-discovery-url:"$WAKU_FLEET" \
+ --dns4-domain-name:"$DOMAIN_NAME" \
+ --discv5-discovery:true
+ ```
+
+ OR
+
+ If you did not set up a domain -
+
+ ```bash
+ $WAKUNODE_DIR/wakunode2 \
+ --store:true \
+ --persist-messages \
+ --dns-discovery \
+ --dns-discovery-url:"$WAKU_FLEET" \
+ --discv5-discovery:true
+ ```
+
+You now have nwaku running! You can verify this by observing the logs. The logs should show that the node completed 7 steps of setup, and is actively discovering other nodes.
+
+You may now detach from stdout, by following instructions according to the terminal multiplexer chosen previously -
+1. [screen](https://linuxize.com/post/how-to-use-linux-screen/#detach-from-linux-screen-session)
+2. [tmux](https://linuxize.com/post/getting-started-with-tmux/#starting-your-first-tmux-session)
+
+To re-attach and observe the logs at a later date, follow these instructions -
+1. [screen](https://linuxize.com/post/how-to-use-linux-screen/#reattach-to-a-linux-screen)
+2. [tmux](https://linuxize.com/post/getting-started-with-tmux/#re-attaching-to-tmux-session)
+
+For alternative configurations, refer to this [guide](./how-to/configure.md)
+
diff --git a/third-party/nwaku/docs/operators/how-to/build.md b/third-party/nwaku/docs/operators/how-to/build.md
new file mode 100644
index 0000000..473547c
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/build.md
@@ -0,0 +1,59 @@
+# Build nwaku
+
+Nwaku can be built on Linux and macOS.
+Windows support is experimental.
+
+## Installing dependencies
+
+Cloning and building nwaku requires the usual developer tools,
+such as a C compiler, Make, Bash and Git.
+
+### Linux
+
+On common Linux distributions the dependencies can be installed with
+
+```sh
+# Debian and Ubuntu
+sudo apt-get install build-essential git
+
+# Fedora
+dnf install @development-tools
+
+# Archlinux, using an AUR manager
+yourAURmanager -S base-devel
+```
+
+### macOS
+
+Assuming you use [Homebrew](https://brew.sh/) to manage packages
+
+```sh
+brew install cmake
+```
+
+## Building nwaku
+
+### 1. Clone the nwaku repository
+
+```sh
+git clone https://github.com/status-im/nwaku
+cd nwaku
+```
+
+### 2. Make the `wakunode2` target
+
+```sh
+# The first `make` invocation will update all Git submodules.
+# You'll run `make update` after each `git pull`, in the future, to keep those submodules up to date.
+make wakunode2
+```
+
+This will create a `wakunode2` binary in the `./build/` directory.
+
+> **Note:** Building `wakunode2` requires 2GB of RAM.
+The build will fail on systems not fulfilling this requirement.
+
+> Setting up a `wakunode2` on the smallest [digital ocean](https://docs.digitalocean.com/products/droplets/how-to/) droplet, you can either
+> * compile on a stronger droplet featuring the same CPU architecture and downgrade after compiling, or
+> * activate swap on the smallest droplet, or
+> * use Docker.
diff --git a/third-party/nwaku/docs/operators/how-to/configure-dns-disc.md b/third-party/nwaku/docs/operators/how-to/configure-dns-disc.md
new file mode 100644
index 0000000..f003c61
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/configure-dns-disc.md
@@ -0,0 +1,27 @@
+# Use DNS discovery to connect to existing nodes
+
+> **Note:** This page describes using DNS to discover other peers
+and is unrelated to the [domain name configuration](./configure-domain.md) for your nwaku node.
+
+A node can discover other nodes to connect to using [DNS-based discovery](../../tutorial/dns-disc.md).
+The following command line options are available:
+
+```
+--dns-discovery Enable DNS Discovery
+--dns-discovery-url URL for DNS node list in format 'enrtree://@'
+```
+
+- `--dns-discovery` is used to enable DNS discovery on the node.
+Waku DNS discovery is disabled by default.
+- `--dns-discovery-url` is mandatory if DNS discovery is enabled.
+It contains the URL for the node list.
+The URL must be in the format `enrtree://@` where `` is the fully qualified domain name and `` is the base32 encoding of the compressed 32-byte public key that signed the list at that location.
+
+A node will attempt connection to all discovered nodes.
+
+This can be used, for example, to connect to one of the existing fleets.
+Current URLs for the published fleet lists:
+- production fleet: `enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im`
+- test fleet: `enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im`
+
+See the [separate tutorial](../../tutorial/dns-disc.md) for a complete guide to DNS discovery.
diff --git a/third-party/nwaku/docs/operators/how-to/configure-domain.md b/third-party/nwaku/docs/operators/how-to/configure-domain.md
new file mode 100644
index 0000000..226da4b
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/configure-domain.md
@@ -0,0 +1,16 @@
+# Configure a domain name
+
+> **Note:** This page describes configuring a domain name that resolves to your node's IP
+and is unrelated to [DNS discovery](./configure-dns-disc.md),
+by which a node may discover the listening addresses of other peers using DNS.
+
+It is possible to configure an IPv4 DNS domain name that resolves to the node's public IPv4 address.
+
+```shell
+wakunode2 --dns4-domain-name=mynode.example.com
+```
+
+This allows for the node's publicly announced `multiaddrs` to use the `/dns4` scheme.
+In addition, nodes with domain name and [secure websocket configured](./configure-websocket.md),
+will generate a discoverable ENR containing the `/wss` multiaddr with `/dns4` domain name.
+This is necessary to verify domain certificates when connecting to this node over secure websocket.
\ No newline at end of file
diff --git a/third-party/nwaku/docs/operators/how-to/configure-key.md b/third-party/nwaku/docs/operators/how-to/configure-key.md
new file mode 100644
index 0000000..8da84f9
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/configure-key.md
@@ -0,0 +1,54 @@
+# Generate and configure a node key
+
+By default a node will generate a new, random key pair each time it boots,
+resulting in a different public libp2p `multiaddrs` after each restart.
+
+To maintain consistent addressing across restarts,
+it is possible to configure the node with a previously generated private key using the `--nodekey` option.
+
+```shell
+wakunode2 --nodekey=<64_char_hex>
+```
+
+This option takes a [Secp256k1](https://en.bitcoin.it/wiki/Secp256k1) private key in 64 char hexstring format.
+
+To generate such a key on Linux systems,
+use the openssl `rand` command to generate a pseudo-random 32 byte hexstring.
+
+```sh
+openssl rand -hex 32
+```
+
+Example output:
+
+```sh
+$ openssl rand -hex 32
+6a29e767c96a2a380bb66b9a6ffcd6eb54049e14d796a1d866307b8beb7aee58
+```
+
+where the key `6a29e767c96a2a380bb66b9a6ffcd6eb54049e14d796a1d866307b8beb7aee58` can be used as `nodekey`.
+
+To create a reusable keyfile on Linux using `openssl`,
+use the `ecparam` command coupled with some standard utilities
+whenever you want to extract the 32 byte private key in hex format.
+
+```sh
+# Generate keyfile
+openssl ecparam -genkey -name secp256k1 -out my_private_key.pem
+# Extract 32 byte private key
+openssl ec -in my_private_key.pem -outform DER | tail -c +8 | head -c 32| xxd -p -c 32
+```
+
+Example output:
+
+```sh
+read EC key
+writing EC key
+0c687bb8a7984c770b566eae08520c67f53d302f24b8d4e5e47cc479a1e1ce23
+```
+
+where the key `0c687bb8a7984c770b566eae08520c67f53d302f24b8d4e5e47cc479a1e1ce23` can be used as `nodekey`.
+
+```sh
+wakunode2 --nodekey=0c687bb8a7984c770b566eae08520c67f53d302f24b8d4e5e47cc479a1e1ce23
+```
diff --git a/third-party/nwaku/docs/operators/how-to/configure-rest-api.md b/third-party/nwaku/docs/operators/how-to/configure-rest-api.md
new file mode 100644
index 0000000..3fe070a
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/configure-rest-api.md
@@ -0,0 +1,23 @@
+
+# Configure a REST API node
+
+A subset of the node configuration can be used to modify the behaviour of the HTTP REST API.
+
+These are the relevant command line options:
+
+| CLI option | Description | Default value |
+|------------|-------------|---------------|
+|`--rest` | Enable Waku REST HTTP server. | `false` |
+|`--rest-address` | Listening address of the REST HTTP server. | `127.0.0.1` |
+|`--rest-port` | Listening port of the REST HTTP server. | `8645` |
+|`--rest-relay-cache-capacity` | Capacity of the Relay REST API message cache. | `30` |
+|`--rest-admin` | Enable access to REST HTTP Admin API. | `false` |
+|`--rest-private` | Enable access to REST HTTP Private API. | `false` |
+
+Note that these command line options have their counterpart option in the node configuration file.
+
+Example:
+
+```shell
+wakunode2 --rest=true
+```
diff --git a/third-party/nwaku/docs/operators/how-to/configure-store-v0.12.0.md b/third-party/nwaku/docs/operators/how-to/configure-store-v0.12.0.md
new file mode 100644
index 0000000..237c7fc
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/configure-store-v0.12.0.md
@@ -0,0 +1,56 @@
+# Configure store protocol (versions prior to v0.13.0)
+
+Store protocol is enabled by default on a nwaku node.
+This is controlled by the `--store` CLI option.
+
+```sh
+# Disable store protocol on startup
+./build/wakunode2 --store:false
+```
+
+Note that this only mounts the `store` protocol,
+meaning your node will indicate to other peers that it supports `store`.
+It does not yet allow your node to either retrieve historical messages as a client
+or store and serve historical messages itself.
+
+## Configuring a store client
+
+Ensure that `store` is enabled (this is `true` by default) and provide at least one store service node address with the `--storenode` CLI option.
+
+See the following example, using the peer at `/dns4/node-01.ac-cn-hongkong-c.waku.test.status.im/tcp/30303/p2p/16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp` as store service node.
+
+```sh
+./build/wakunode2 \
+ --store:true \
+ --storenode:/dns4/node-01.ac-cn-hongkong-c.waku.test.status.im/tcp/30303/p2p/16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp
+```
+
+Your node can now send queries to retrieve historical messages
+from the configured store service node.
+One way to trigger such queries is asking your node for historical messages using the [Waku v2 JSON RPC API](https://rfc.vac.dev/spec/16/).
+
+## Configuring a store service node
+
+To store historical messages on your node which can be served to store clients the `--persist-messages` CLI option must be enabled.
+By default a node would store up to the latest `50 000` messages.
+This is configurable using the `--store-capacity` option.
+A node that has a `--db-path` set will backup historical messages to a local database at the DB path
+and persist these messages even after a restart.
+
+```sh
+./build/wakunode2 \
+ --store:true \
+ --persist-messages:true \
+ --db-path:/mnt/nwaku/data/db1/ \
+ --store-capacity:150000
+```
+
+### How much resources should I allocate?
+
+Currently store service nodes use an in-memory key-value store as primary storage with the disk-based database only used for backups.
+Most Waku messages average a size of 1KB - 2KB,
+implying a minimum memory requirement of at least ~250MB
+for a medium capacity store of 100k messages.
+Note, however, that the allowable maximum size for Waku messages is up to 1MB.
+We are working on a disk-only and hybrid store to lower the memory requirement.
+It will soon also be possible to configure store capacity on maximum store size or number of days' history to keep.
diff --git a/third-party/nwaku/docs/operators/how-to/configure-store.md b/third-party/nwaku/docs/operators/how-to/configure-store.md
new file mode 100644
index 0000000..ffa39cf
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/configure-store.md
@@ -0,0 +1,58 @@
+# Configure store protocol
+
+> :information_source: This instructions apply to nwaku version v0.13.0+. For versions prior to v0.13.0, check [this page](./configure-store-v0.12.0.md).
+
+The waku store protocol is disabled by default the nwaku node.
+This is controlled by the `--store` option. To enable waku store protocol on startup, specify explicitly the `--store` option set to `true`:
+
+```shell
+wakunode2 --store=true
+```
+
+This option controls the mounting of the Waku Store protocol, meaning that your node will indicate to other peers that it supports the Waku store protocol.
+
+## Configuring the node as a waku store client
+
+Provide at least one store service node address with the `--storenode` option. This option is independent of the `--store` option i.e., one node can act as a waku store client without mounting the Waku Store protocol.
+
+For example, to use the peer at `/dns4/node-01.ac-cn-hongkong-c.waku.test.status.im/tcp/30303/p2p/16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp` as the waku store service node:
+
+```shell
+wakunode2 \
+ --storenode=/dns4/node-01.ac-cn-hongkong-c.waku.test.status.im/tcp/30303/p2p/16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp
+```
+
+Your node can now send queries to retrieve historical messages
+from the configured store service node. One way to trigger such queries is asking your node for historical messages using the [Waku v2 JSON RPC API](https://rfc.vac.dev/spec/16/).
+
+## Configuring the node as a store service node
+
+If the waku store node is enabled (the `--store` option is set to `true`) the node will store historical messages and will be able to serve those messages to the waku store clients.
+
+There is a set of configuration options to customize the waku store protocol's message store. These are the most relevant:
+
+* `--store-message-retention-policy`: This option controls the retention policy i.e., how long certain messages will be persisted. Three different retention policies are supported:
+ + The time retention policy,`time:` (e.g., `time:14400`)
+ + The capacity retention policy,`capacity:` (e.g, `capacity:25000`)
+ + The size retention policy,`size:` (e.g, `size:25Gb`)
+ + To disable the retention policy, explicitly, set this option to `""`, an empty string.
+* `--store-message-db-url`: The message store database url option controls the message storage engine. This option follows the [_SQLAlchemy_ database URL format](https://docs.sqlalchemy.org/en/14/core/engines.html#database-urls).
+
+ + SQLite engine: The only database engine supported by the nwaku node. The database URL has this shape: `sqlite://`. If the `` is not an absolute path (preceded by a `/` character), the file will be created in the current working directory. The SQLite engine also supports to select a non-persistent in-memory database by setting the `` to `:memory:`.
+ + In the case you don't want to use a persistent message store; set the `--store-message-db-url` to an empty string, `""`. This will instruct the node to use the fallback in-memory message store.
+
+By default the node message store will be configured with a time retention policy set to `14400` seconds (4 hours). Additionally, by default, the node message store will use the SQLite database engine to store historical messages in order to persist these between restarts.
+
+> :warning: Note the 3 slashes, `///`, after the SQLite database URL schema. The third slash indicates that it is an absolute path: `/mnt/nwaku/data/db1/store.sqlite3`
+
+```shell
+wakunode2 \
+ --store=true \
+ --store-message-db-url=sqlite:///mnt/nwaku/data/db1/store.sqlite3 \
+ --store-message-retention-policy=capacity:150000
+```
+
+### How much resources should I allocate?
+
+Currently store service nodes use, by default, a message store backed by an in-disk SQLite database. Most Waku messages average a size of 1KB - 2KB, implying a minimum memory requirement of at least ~250MB
+for a typical store capacity of 100k messages. Note, however, that the allowable maximum size for Waku messages is up to 1MB.
diff --git a/third-party/nwaku/docs/operators/how-to/configure-websocket.md b/third-party/nwaku/docs/operators/how-to/configure-websocket.md
new file mode 100644
index 0000000..4f1c461
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/configure-websocket.md
@@ -0,0 +1,35 @@
+# Configure websocket transport
+
+Websocket is currently the only Waku transport supported by browser nodes using [js-waku](https://github.com/status-im/js-waku).
+Setting up websocket enables your node to directly serve browser peers.
+
+A valid certificate is necessary to serve browser nodes,
+you can use [`letsencrypt`](https://letsencrypt.org/):
+
+```shell
+sudo letsencrypt -d
+```
+
+You will need the `privkey.pem` and `fullchain.pem` files.
+
+To enable secure websocket, pass the generated files to `wakunode2`:
+Note, the default port for websocket is 8000.
+
+```shell
+wakunode2 --websocket-secure-support=true --websocket-secure-key-path="/privkey.pem" --websocket-secure-cert-path="/fullchain.pem"
+```
+
+## Self-signed certificates
+
+Self-signed certificates are not recommended for production setups because:
+
+- Browsers do not accept self-signed certificates
+- Browsers do not display an error when rejecting a certificate for websocket.
+
+However, they can be used for local testing purposes:
+
+```shell
+mkdir -p ./ssl_dir/
+openssl req -x509 -newkey rsa:4096 -keyout ./ssl_dir/key.pem -out ./ssl_dir/cert.pem -sha256 -nodes
+wakunode2 --websocket-secure-support=true --websocket-secure-key-path="./ssl_dir/key.pem" --websocket-secure-cert-path="./ssl_dir/cert.pem"
+```
\ No newline at end of file
diff --git a/third-party/nwaku/docs/operators/how-to/configure.md b/third-party/nwaku/docs/operators/how-to/configure.md
new file mode 100644
index 0000000..f052b22
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/configure.md
@@ -0,0 +1,136 @@
+# Configure a nwaku node
+
+Nwaku can be configured to serve the adaptive needs of different operators.
+
+> :bulb: **Tip:** The recommended configuration method is through environment variables.
+
+## Node configuration methods
+
+One node can be configured using a combination of the following methods:
+
+1. Command line options and flags
+2. Environment variables
+3. Configuration file (currently, only TOML format is supported)
+4. Default value
+
+Note the precedence order, each configuration mechanism overrides the configuration set by one below (e.g., _command line options_ override the configuration set by the _environment variables_ and by the _configuration file_).
+
+### Command line options/flags
+
+The main mechanism to configure the node is via command line options. Any configuration option provided via the command line will override any other configuration mechanism.
+
+> :warning: nwaku is under heavy development. It is likely that configuration will change from one version to another.
+>
+> If after an upgrade, the node refuses to start, check if any of the command line configuration options provided to the node have been changed or removed.
+>
+> To overcome this issue, we recommend to configure the node via environment variables.
+
+The configuration options should be provided after the binary name as follows:
+
+```shell
+wakunode2 --tcp-port=65000
+```
+
+In the case of using docker to run you node you should provide the commandline options after the image name as follows:
+
+```shell
+docker run wakuorg/nwaku --tcp-port=65000
+```
+
+Run `wakunode2 --help` to get a comprehensive list of configuration options (and its default values):
+
+```shell
+$ wakunode2 --help
+Usage:
+
+wakunode2 [OPTIONS]...
+
+The following options are available:
+
+ --config-file Loads configuration from a TOML file (cmd-line parameters take precedence).
+ --log-level Sets the log level. [=LogLevel.INFO].
+ --version prints the version [=false].
+
+<...>
+```
+
+Check the configuration tutorials for specific configuration use cases.
+
+### Environment variables
+
+The node can also be configured via environment variables.
+
+> :information_source: Support for configuring the node via environment variables was added in v0.13.0
+
+The environment variable name should be prefixed by the app's name, in this case `WAKUNODE2_` followed by the commandline option in [screaming snake case](https://en.wiktionary.org/wiki/screaming_snake_case).
+
+For example, to set the `--tcp-port` configuration we should call `wakunode2` binary as follows:
+
+```shell
+WAKUNODE2_TCP_PORT=65000 wakunode2
+```
+
+In the case of using docker to run you node you should start the node using the `-e` command options:
+
+```shell
+docker run -e "WAKUNODE2_TCP_PORT=65000" wakuorg/nwaku
+```
+
+This is the second configuration method in order of precedence. Any command line configuration option will override the configuration
+provided via environment variables.
+
+### Configuration file
+
+The third configuration mechanism in order of precedence is the configuration via a TOML file. The previous mechanisms take precedence over this mechanism as explained above.
+
+The configuration file follows the [TOML](https://toml.io/en/) format:
+
+```toml
+log-level = "DEBUG"
+tcp-port = 65000
+```
+
+The path to the TOML file can be specified using one of the previous configuration mechanisms:
+
+* By passing the `--config-file` command line option:
+ ```shell
+ wakunode2 --config-file=
+ ```
+* By passing the path via environment variables:
+ ```shell
+ WAKUNODE2_CONFIG_FILE= wakunode2
+ ```
+
+### Configuration default values
+
+As usual, if no configuration option is specified by any of the previous mechanisms, the default configuration will be used.
+
+The default configuration value is listed in the `wakunode2 --help` output:
+
+```shell
+$ wakunode2 --help
+Usage:
+
+wakunode2 [OPTIONS]...
+
+The following options are available:
+
+ --config-file Loads configuration from a TOML file (cmd-line parameters take precedence).
+ --log-level Sets the log level. [=LogLevel.INFO].
+ --version prints the version [=false].--tcp-port TCP listening port. [=60000].
+ --websocket-port WebSocket listening port. [=8000].
+<...>
+```
+
+## Configuration use cases
+
+This is an index of tutorials explaining how to configure your nwaku node for different use cases.
+
+1. [Connect to other peers](./connect.md)
+2. [Configure a domain name](./configure-domain.md)
+3. [Use DNS discovery to connect to existing nodes](./configure-dns-disc.md)
+4. [Configure store protocol and message store](./configure-store.md)
+5. [Generate and configure a node key](./configure-key.md)
+6. [Configure websocket transport](./configure-websocket.md)
+7. [Run nwaku with rate limiting enabled](./run-with-rln.md)
+8. [Configure a REST API node](./configure-rest-api.md)
diff --git a/third-party/nwaku/docs/operators/how-to/connect.md b/third-party/nwaku/docs/operators/how-to/connect.md
new file mode 100644
index 0000000..b375401
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/connect.md
@@ -0,0 +1,60 @@
+# Connect to other peers
+
+*Note that this tutorial describes how to **configure** a node to connect to other peers before running the node.
+For connecting a running node to existing peers,
+see the [WAKU REST API reference](https://waku-org.github.io/waku-rest-api/#post-/admin/v1/peers).*
+
+There are currently three options.
+Note that each of these options can be used in combination with any of the other two.
+In other words, it is possible to configure a node to connect
+to a static list of peers and
+to discover such peer lists using DNS discovery and
+discover and connect to random peers using discovery v5 with a bootstrap node.
+
+## Option 1: Configure peers statically
+
+Static peers can be provided to a nwaku node on startup using the `--staticnode` CLI parameter.
+The `--staticnode` option can be repeated for each peer you want to connect to on startup.
+
+```sh
+./build/wakunode2 \
+ --staticnode: \
+ --staticnode:
+```
+
+As an example, consider a nwaku node that connects to two known peers
+on the same local host (with IP `0.0.0.0`)
+with TCP ports `60002` and `60003`,
+and peer IDs `16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H` and `16Uiu2HAmFBA7LGtwY5WVVikdmXVo3cKLqkmvVtuDu63fe8safeQJ` respectively.
+
+```sh
+./build/wakunode2 \
+ --staticnode:/ip4/0.0.0.0/tcp/60002/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H \
+ --staticnode:/ip4/0.0.0.0/tcp/60003/p2p/16Uiu2HAmFBA7LGtwY5WVVikdmXVo3cKLqkmvVtuDu63fe8safeQJ
+```
+
+## Option 2: Discover peers using DNS discovery
+
+A node can discover other nodes to connect to using DNS-based discovery.
+For a quickstart guide on how to configure DNS discovery,
+see [this tutorial](./configure-dns-disc.md).
+There is also a [more comprehensive tutorial](../../tutorial/dns-disc.md) for advanced users.
+
+## Option 3: Discover peers using Waku Discovery v5
+
+
+
+Enable Discovery v5 using the `--discv5-discovery` option.
+
+It is possible to configure bootstrap entries for the Discovery v5 routing table
+using the `--discv5-bootstrap-node` option repeatedly.
+
+```sh
+./build/wakunode2 \
+ --discv5-discovery:true \
+ --discv5-bootstrap-node: \
+ --discv5-bootstrap-node:
+```
+
+Note that if Discovery v5 is enabled and used in conjunction with DNS-based discovery,
+the nwaku node will attempt to bootstrap the Discovery v5 routing table with ENRs extracted from the peers discovered via DNS.
diff --git a/third-party/nwaku/docs/operators/how-to/monitor.md b/third-party/nwaku/docs/operators/how-to/monitor.md
new file mode 100644
index 0000000..8ef3f06
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/monitor.md
@@ -0,0 +1,111 @@
+# Monitor nwaku using Prometheus and Grafana
+
+## Prerequisites
+
+1. A running nwaku instance with HTTP metrics server enabled (i.e. with `--metrics-server:true`)
+2. [Prometheus](https://prometheus.io/) and [Grafana](https://grafana.com/) installed
+
+### Installing Prometheus
+
+Prometheus can be installed by downloading and extracting
+the latest release for your system distribution from the [Prometheus download page](https://prometheus.io/download/).
+
+For example, on a DebianOS distribution you could run
+
+```bash
+wget https://github.com/prometheus/prometheus/releases/download/v2.38.0/prometheus-2.38.0.linux-amd64.tar.gz
+tar xvfz prometheus-2.38.0.linux-amd64.tar.gz
+```
+
+For more advanced installations,
+Prometheus has a handy [Getting Started](https://prometheus.io/docs/prometheus/latest/getting_started/) page to guide you through the process.
+There are also many third party guides on installing Prometheus for specific distributions,
+such as [this old but still relevant one](https://www.digitalocean.com/community/tutorials/how-to-install-prometheus-on-ubuntu-16-04) from DigitalOcean.
+We also suggest running Prometheus as a service,
+as explained by [this guide](https://www.devopsschool.com/blog/how-to-run-prometheus-server-as-a-service/).
+Bear in mind that we'll be creating our own `prometheus.yml` configuration file later on when you encounter this in any of the guides.
+
+### Installing Grafana
+
+Follow the [installation instructions](https://grafana.com/docs/grafana/latest/setup-grafana/installation/) appropriate to your distribution to install Grafana.
+The stable version of the Grafana Enterprise Edition is the free, recommended edition to install.
+
+## Configure Prometheus
+
+1. Create a file called `prometheus.yml` with the following content:
+
+```yml
+global:
+ scrape_interval: 15s
+
+scrape_configs:
+ - job_name: 'prometheus'
+ scrape_interval: 5s
+ static_configs:
+ - targets: ['localhost:9090']
+ - job_name: 'nwaku'
+ scrape_interval: 1s
+ static_configs:
+ - targets: ['localhost:']
+```
+
+Replace `` with the metrics HTTP server port of your running nwaku instance.
+For default configurations metrics are reported on port `8008` of the `localhost`.
+If you've used `--ports-shift`, or explicitly set the metrics port using `--metrics-server-port`, this port will be different from the default.
+It's possible to extract the metrics server port from the startup logs of the nwaku node.
+Look for a log with the format below and substitute `nwaku_port` with the value reported after `serverPort=`:
+
+```
+INF 2022-09-16 12:14:12.739+01:00 Metrics HTTP server started topics="wakunode.setup.metrics" tid=6243 file=wakunode2_setup_metrics.nim:29 serverIp=127.0.0.1 serverPort=8009
+```
+
+2. Start Prometheus using the config file you created above:
+
+```bash
+./path/to/prometheus --config.file=/path/to/prometheus.yml &
+```
+
+3. Verify that Prometheus is running correctly.
+
+Once Prometheus is running, it exposes by default a management console on port `9090`.
+If you are running Prometheus locally, for example,
+you can visit http://localhost:9090/ in a browser to view basic info about the running instance.
+http://localhost:9090/targets shows the state of the different metrics server endpoints that we configured in `prometheus.yml`.
+In our case we'd expect Prometheus to successfully scrape metrics off two endpoints,
+the running nwaku instance and Prometheus itself.
+
+## Configure Grafana
+
+1. Start the Grafana server, if it's not running already after installation.
+
+```bash
+sudo systemctl start grafana-server
+```
+
+2. Open Grafana in your browser.
+
+Grafana exposes its interface by default on port `3000`.
+For example, if you are running Grafana locally,
+you can find it by navigating to http://localhost:3000/.
+If you are prompted for a username and password,
+the default is `admin` in both cases.
+
+3. Set Prometheus as your data source.
+
+[These instructions](https://grafana.com/docs/grafana/latest/datasources/add-a-data-source/) describe how to add a new data source.
+The default values for setting up a Prometheus data source should be sufficient.
+
+4. Create a new dashboard or import an existing one.
+
+You can now visualize metrics off your running nwaku instance by [creating a new dashboard and adding panels](https://grafana.com/docs/grafana/latest/dashboards/add-organize-panels/) for the metric(s) of your choice.
+To get you started,
+we have published a [basic monitoring dashboard for a single nwaku node](https://github.com/status-im/nwaku/blob/d4e899fba77389d20ca19c73a9443501039cdef2/metrics/waku-single-node-dashboard.json)
+which you can [import to your Grafana instance](https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/#import-a-dashboard).
+
+5. Happy monitoring!
+
+Some of the most important metrics to keep an eye on include:
+- `libp2p_peers` as an indication of how many peers your node is connected to,
+- `waku_node_messages_total` to view the total amount of network traffic relayed by your node and
+- `waku_node_errors` as a rough indication of basic operating errors logged by the node.
+
diff --git a/third-party/nwaku/docs/operators/how-to/run-with-rln.md b/third-party/nwaku/docs/operators/how-to/run-with-rln.md
new file mode 100644
index 0000000..ef1a6c2
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/run-with-rln.md
@@ -0,0 +1,89 @@
+# How to run spam prevention on your nwaku node (RLN)
+
+This guide explains how to run a nwaku node with RLN (Rate Limiting Nullifier) enabled.
+
+[RLN](https://rfc.vac.dev/spec/32/) is a protocol integrated into waku v2,
+which prevents spam-based attacks on the network.
+
+For further background on the research for RLN tailored to waku, refer
+to [this](https://rfc.vac.dev/spec/17/) RFC.
+
+Registering to the membership group has been left out for brevity.
+If you would like to register to the membership group and send messages with RLN,
+refer to the [on-chain chat2 tutorial](../../tutorial/onchain-rln-relay-chat2.md).
+
+This guide specifically allows a node to participate in RLN testnet 2.
+You may alter the rln-specific arguments as required.
+
+## Prerequisites
+
+1. Follow the [droplet quickstart](../droplet-quickstart.md) or the [build guide](./build.md) till the `make` command for the wakunode2 binary.
+
+> Note: If you would like to run a nwaku node with RLN enabled within a docker container, skip ahead to step 2.
+
+## 1. Build wakunode2
+
+Run -
+```bash
+make wakunode2
+```
+
+## 2. Update the runtime arguments
+
+Follow [Step 10](../droplet-quickstart.md#10-run-nwaku) of the [droplet quickstart](../droplet-quickstart.md) guide, while replacing the run command with -
+
+```bash
+export LINEA_SEPOLIA_HTTP_NODE_ADDRESS=
+export RLN_RELAY_CONTRACT_ADDRESS="0xB9cd878C90E49F797B4431fBF4fb333108CB90e6" # Replace this with any compatible implementation
+$WAKUNODE_DIR/wakunode2 \
+--store:true \
+--persist-messages \
+--dns-discovery \
+--dns-discovery-url:"$WAKU_FLEET" \
+--discv5-discovery:true \
+--rln-relay:true \
+--rln-relay-dynamic:true \
+--rln-relay-eth-contract-address:"$RLN_RELAY_CONTRACT_ADDRESS" \
+--rln-relay-eth-client-address:"$LINEA_SEPOLIA_HTTP_NODE_ADDRESS"
+```
+
+OR
+
+If you are running the nwaku node within docker, follow [Step 2](../docker-quickstart.md#step-2-run) while replacing the run command with -
+
+```bash
+export WAKU_FLEET=
+export LINEA_SEPOLIA_HTTP_NODE_ADDRESS=
+export RLN_RELAY_CONTRACT_ADDRESS="0xB9cd878C90E49F797B4431fBF4fb333108CB90e6" # Replace this with any compatible implementation
+docker run -i -t -p 60000:60000 -p 9000:9000/udp wakuorg/nwaku:v0.36.0 \
+ --dns-discovery:true \
+ --dns-discovery-url:"$WAKU_FLEET" \
+ --discv5-discovery \
+ --nat:extip:[yourpublicip] \ # or, if you are behind a nat: --nat=any
+ --rln-relay:true \
+ --rln-relay-dynamic:true \
+ --rln-relay-eth-contract-address:"$RLN_RELAY_CONTRACT_ADDRESS" \
+ --rln-relay-eth-client-address:"$LINEA_SEPOLIA_HTTP_NODE_ADDRESS"
+```
+
+> Note: You can choose to keep connections to other nodes alive by adding the `--keep-alive` flag.
+
+Following is the list of additional fields that have been added to the
+runtime arguments -
+
+1. `--rln-relay`: Allows waku-rln-relay to be mounted into the setup of the nwaku node
+2. `--rln-relay-dynamic`: Enables waku-rln-relay to connect to an ethereum node to fetch the membership group
+3. `--rln-relay-eth-contract-address`: The contract address of an RLN membership group
+4. `--rln-relay-eth-client-address`: The HTTP url to a Linea Sepolia ethereum node
+
+You should now have nwaku running, with RLN enabled!
+
+To see metrics related to the functioning of RLN, refer to this [guide](./todo).
+You can also refer to the periodic logging, for a few metrics like -
+
+- number of spam messages
+- number of valid messages
+- number of invalid messages
+
+
+> Note: This guide will be updated in the future to include features like slashing.
diff --git a/third-party/nwaku/docs/operators/how-to/run.md b/third-party/nwaku/docs/operators/how-to/run.md
new file mode 100644
index 0000000..bc90394
--- /dev/null
+++ b/third-party/nwaku/docs/operators/how-to/run.md
@@ -0,0 +1,196 @@
+# Running nwaku
+
+Nwaku binaries can be [built](./build.md) and run on Linux and macOS.
+Windows support is experimental.
+
+```sh
+# Run with default configuration
+./build/wakunode2
+
+# See available command line options
+./build/wakunode2 --help
+```
+
+## Default configuration
+
+By default a nwaku node will:
+- generate a new private key and libp2p identities after every restart.
+See [this tutorial](./configure-key.md) if you want to generate and configure a persistent private key.
+- listen for incoming libp2p connections on the default TCP port (`60000`)
+- enable `relay` protocol
+- subscribe to the default clusterId (0) and shard (0)
+- enable `store` protocol, but only as a client.
+This implies that the nwaku node will not persist any historical messages itself,
+but can query `store` service peers who do so.
+To configure `store` as a service node,
+see [this tutorial](./configure-store.md).
+
+> **Note:** The `filter` and `lightpush` protocols are _not_ enabled by default.
+Consult the [configuration guide](./configure.md) on how to configure your nwaku node to run these protocols.
+
+Some typical non-default configurations are explained below.
+For more advanced configuration, see the [configuration guide](./configure.md).
+Different ways to connect to other nodes are expanded upon in our [connection guide](./connect.md).
+
+## Finding your listening address(es)
+
+Find the log entry beginning with `Listening on`.
+It should be printed at INFO level when you start your node
+and contains a list of all publicly announced listening addresses for the nwaku node.
+
+For example
+
+```
+INF 2022-05-11 16:42:30.591+02:00 Listening on topics="wakunode" tid=6661 file=wakunode2.nim:941 full=[/ip4/0.0.0.0/tcp/60000/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H][/ip4/0.0.0.0/tcp/8000/ws/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H]
+```
+
+indicates that your node is listening on the TCP transport address
+
+```
+/ip4/0.0.0.0/tcp/60000/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H
+```
+
+and websocket address
+
+```
+/ip4/0.0.0.0/tcp/8000/ws/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H
+```
+
+You can also query a running node for its listening addresses
+using the REST API.
+
+```bash
+curl http://localhost:8645/debug/v1/info -s | jq
+```
+
+## Finding your discoverable ENR address(es)
+
+A nwaku node can encode its addressing information in an [Ethereum Node Record (ENR)](https://eips.ethereum.org/EIPS/eip-778) according to [`31/WAKU2-ENR`](https://rfc.vac.dev/spec/31/).
+These ENR are most often used for discovery purposes.
+
+### ENR for DNS discovery
+
+Find the log entry beginning with `DNS: discoverable ENR`.
+It should be printed at INFO level when you start your node with [DNS discovery enabled](./configure-dns-disc.md)
+and contains an ENR that can be added to node lists discoverable via DNS.
+
+For example
+
+```
+INF 2022-05-20 11:52:48.772+02:00 DNS: discoverable ENR topics="wakunode" tid=5182 file=wakunode2.nim:941 enr=enr:-Iu4QBZs5huNuEAjI9WA0HOAjzpmp39vKJAtYRG3HXH86-i3HGcxMgupIkyDBmBq9qJ2wFfgMiW8AUzUxTFMAzfJM5MBgmlkgnY0gmlwhAAAAACJc2VjcDI1NmsxoQN0EcrUbHrL_O_kNXDlBvcO1I4yZUdNk7VZI5GsXaWgvYN0Y3CC6mCFd2FrdTID
+```
+
+indicates that your node addresses are encoded in the ENR
+
+```
+enr=enr:-Iu4QBZs5huNuEAjI9WA0HOAjzpmp39vKJAtYRG3HXH86-i3HGcxMgupIkyDBmBq9qJ2wFfgMiW8AUzUxTFMAzfJM5MBgmlkgnY0gmlwhAAAAACJc2VjcDI1NmsxoQN0EcrUbHrL_O_kNXDlBvcO1I4yZUdNk7VZI5GsXaWgvYN0Y3CC6mCFd2FrdTID
+```
+
+### ENR for Discovery v5
+
+Find the log entry beginning with `Discv5: discoverable ENR`.
+It should be printed at INFO level when you start your node with [Waku Discovery v5 enabled](https://rfc.vac.dev/spec/33/)
+and contains the ENR that will be discoverable by other peers.
+
+For example
+
+```
+INF 2022-05-20 11:52:48.775+02:00 Discv5: discoverable ENR topics="wakunode" tid=5182 file=wakunode2.nim:905 enr=enr:-IO4QDxToTg86pPCK2KvMeVCXC2ADVZWrxXSvNZeaoa0JhShbM5qed69RQz1s1mWEEqJ3aoklo_7EU9iIBcPMVeKlCQBgmlkgnY0iXNlY3AyNTZrMaEDdBHK1Gx6y_zv5DVw5Qb3DtSOMmVHTZO1WSORrF2loL2DdWRwgiMohXdha3UyAw
+```
+
+indicates that your node addresses are encoded in the ENR
+
+```
+enr=enr:-IO4QDxToTg86pPCK2KvMeVCXC2ADVZWrxXSvNZeaoa0JhShbM5qed69RQz1s1mWEEqJ3aoklo_7EU9iIBcPMVeKlCQBgmlkgnY0iXNlY3AyNTZrMaEDdBHK1Gx6y_zv5DVw5Qb3DtSOMmVHTZO1WSORrF2loL2DdWRwgiMohXdha3UyAw
+```
+
+## Typical configuration (relay node)
+
+The typical configuration for a nwaku node is to run the `relay` protocol,
+subscribed to the default pubsub topic `/waku/2/rs/0/0`,
+and connecting to one or more existing peers.
+We assume below that running nodes also participate in Discovery v5
+to continually discover and connect to random peers for a more robust mesh.
+
+### Connecting to known peer(s)
+
+A typical run configuration for a nwaku node is to connect to existing peers with known listening addresses using the `--staticnode` option.
+The `--staticnode` option can be repeated for each peer you want to connect to on startup.
+This is also useful if you want to run several nwaku instances locally
+and therefore know the listening addresses of all peers.
+
+As an example, consider a nwaku node that connects to two known peers
+on the same local host (with IP `0.0.0.0`)
+with TCP ports `60002` and `60003`,
+and peer IDs `16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H` and `16Uiu2HAmFBA7LGtwY5WVVikdmXVo3cKLqkmvVtuDu63fe8safeQJ` respectively.
+The Discovery v5 routing table can similarly be bootstrapped using a static ENR.
+We include an example below.
+
+```sh
+./build/wakunode2 \
+ --ports-shift:1 \
+ --staticnode:/ip4/0.0.0.0/tcp/60002/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H \
+ --staticnode:/ip4/0.0.0.0/tcp/60003/p2p/16Uiu2HAmFBA7LGtwY5WVVikdmXVo3cKLqkmvVtuDu63fe8safeQJ \
+ --discv5-discovery:true \
+ --discv5-bootstrap-node:enr:-JK4QM2ylZVUhVPqXrqhWWi38V46bF2XZXPSHh_D7f2PmUHbIw-4DidCBnBnm-IbxtjXOFbdMMgpHUv4dYVH6TgnkucBgmlkgnY0gmowhCJ6_HaJc2VjcDI1NmsxoQM06FsT6EJ57mzR_wiLu2Bz1dER2nUFSCpaFzCccQtnhYN0Y3CCdl-DdWRwgiMohXdha3UyDw
+```
+
+> **Tip:** `--ports-shift` shifts all configured ports forward by the configured amount.
+This is another useful option when running several nwaku instances on a single machine
+and would like to avoid port clashes without manually configuring each port.
+
+### Connecting to the `waku.sandbox` network
+
+*See [this explainer](https://github.com/status-im/nwaku/blob/6ebe26ad0587d56a87a879d89b7328f67f048911/docs/contributors/waku-fleets.md) on the different networks and Waku v2 fleets.*
+
+You can use DNS discovery to bootstrap connection to the existing production network.
+Discovery v5 will attempt to extract the ENRs of the discovered nodes as bootstrap entries to the routing table.
+
+```sh
+./build/wakunode2 \
+ --ports-shift:1 \
+ --dns-discovery:true \
+ --dns-discovery-url:enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im \
+ --discv5-discovery:true
+```
+
+### Connecting to the `waku.test` network
+
+*See [this explainer](https://github.com/status-im/nwaku/blob/6ebe26ad0587d56a87a879d89b7328f67f048911/docs/contributors/waku-fleets.md) on the different networks and Waku v2 fleets.*
+
+You can use DNS discovery to bootstrap connection to the existing test network.
+Discovery v5 will attempt to extract the ENRs of the discovered nodes as bootstrap entries to the routing table.
+
+```sh
+./build/wakunode2 \
+ --ports-shift:1 \
+ --dns-discovery:true \
+ --dns-discovery-url:enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im \
+ --discv5-discovery:true
+```
+
+## Typical configuration (relay and store service node)
+
+Often nwaku nodes choose to also store historical messages
+from where it can be queried by other peers who may have been temporarily offline.
+For example, a typical configuration for such a store service node,
+[connecting to the `waku.test`](#connecting-to-the-wakutest-network) fleet on startup,
+appears below.
+
+```sh
+./build/wakunode2 \
+ --ports-shift:1 \
+ --store:true \
+ --persist-messages:true \
+ --db-path:/mnt/nwaku/data/db1/ \
+ --store-capacity:150000 \
+ --dns-discovery:true \
+ --dns-discovery-url:enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im \
+ --discv5-discovery:true
+```
+
+See our [store configuration tutorial](./configure-store.md) for more.
+
+## Interact with a running nwaku node
+
+A running nwaku node can be interacted with using the [REST API](https://github.com/waku-org/nwaku/blob/master/docs/api/rest-api.md).
diff --git a/third-party/nwaku/docs/operators/overview.md b/third-party/nwaku/docs/operators/overview.md
new file mode 100644
index 0000000..f43652a
--- /dev/null
+++ b/third-party/nwaku/docs/operators/overview.md
@@ -0,0 +1,39 @@
+# Overview: running a nwaku node
+
+This guide provides on overview for newcomers
+on how to build and run a nwaku node
+for the most common use cases.
+For a more advanced configuration see our [configuration guides](./how-to/configure.md)
+
+To set up a nwaku node on a DigitalOcean droplet,
+refer to our [quickstart guide for droplets](./droplet-quickstart.md).
+If you prefer running nwaku in Docker container,
+see our [Docker guide](./docker-quickstart.md).
+
+## 1. Build
+
+[Build the nwaku node](./how-to/build.md)
+or download a precompiled binary from our [releases page](https://github.com/waku-org/nwaku/releases).
+
+If you'd like to test latest changes without building the binaries yourself, you can refer to [nightly release](https://github.com/waku-org/nwaku/releases/tag/nightly).
+
+Docker images are published to [wakuorg/nwaku](https://hub.docker.com/r/wakuorg/nwaku/tags) on Docker Hub.
+See our [Docker quickstart guide](./docker-quickstart.md) to run nwaku in a Docker container.
+
+## 2. Run
+
+[Run the nwaku node](./how-to/run.md) using a default or common configuration
+or [configure](./how-to/configure.md) the node for more advanced use cases.
+
+[Connect](./how-to/connect.md) the nwaku node to other peers to start communicating.
+
+## 3. Interact
+
+A running nwaku node can be interacted with using the [REST API](../api/v2/rest-api.md).
+
+> **Note:** REST API functionality is in ALPHA and therefore it is disabled by default. To configure a nwaku node with this enabled, use the `--rest:true` CLI option.
+
+
+```bash
+curl http://localhost:8546/debug/v1/info -s | jq
+```
diff --git a/third-party/nwaku/docs/operators/quickstart.md b/third-party/nwaku/docs/operators/quickstart.md
new file mode 100644
index 0000000..47c6195
--- /dev/null
+++ b/third-party/nwaku/docs/operators/quickstart.md
@@ -0,0 +1,61 @@
+# Quickstart: running a nwaku node
+
+This guide helps you run a nwaku node with typical configuration.
+It connects your node to the `waku.sandbox` fleet for bootstrapping
+and enables discovery v5 for continuous peer discovery.
+Only [`relay`](https://rfc.vac.dev/spec/11/) protocol is enabled.
+For a more comprehensive overview,
+see our [step-by-step guide](./overview.md).
+
+## Option 1: run nwaku binary
+
+*Prerequisites are the usual developer tools,
+such as a C compiler, Make, Bash and Git.*
+
+```bash
+git clone --recurse-submodules https://github.com/waku-org/nwaku
+cd nwaku
+make wakunode2
+./build/wakunode2 \
+ --dns-discovery:true \
+ --dns-discovery-url:enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im \
+ --discv5-discovery \
+ --nat=extip:[yourpublicip] # or, if you are behind a nat: --nat=any
+```
+
+## Option 2: run nwaku in a Docker container
+
+*Prerequisite is a [Docker installation](./docker-quickstart.md#prerequisites).*
+
+```bash
+docker run -i -t -p 60000:60000 -p 9000:9000/udp \
+ wakuorg/nwaku:v0.20.0 \ # or, the image:tag of your choice
+ --dns-discovery:true \
+ --dns-discovery-url:enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im \
+ --discv5-discovery \
+ --nat:extip:[yourpublicip] # or, if you are behind a nat: --nat=any
+```
+
+## Option 3: run nwaku with docker compose
+
+*Prerequisites: `docker` and `docker-compose`*.
+Allows to run `nwaku` with `prometheus` and `grafana`, with an already provisioned dashboard, in a few simple steps.
+See [nwaku-compose](https://github.com/waku-org/nwaku-compose).
+
+```bash
+git clone https://github.com/waku-org/nwaku-compose
+cd nwaku-compose
+docker-compose up -d
+```
+
+Go to [http://localhost:3000/d/yns_4vFVk/nwaku-monitoring?orgId=1](http://localhost:3000/d/yns_4vFVk/nwaku-monitoring?orgId=1) and after some seconds, your node metrics will be live there.
+As simple as that.
+
+## Tips and tricks
+
+To find the public IP of your host,
+you can use
+
+```bash
+dig TXT +short o-o.myaddr.l.google.com @ns1.google.com | awk -F'"' '{ print $2}'
+```
diff --git a/third-party/nwaku/docs/tutorial/chat2.md b/third-party/nwaku/docs/tutorial/chat2.md
new file mode 100644
index 0000000..f6489f9
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/chat2.md
@@ -0,0 +1,212 @@
+# Using the `chat2` application
+
+## Background
+
+The `chat2` application is a basic command-line chat app using the [Waku v2 suite of protocols](https://rfc.vac.dev/).
+It optionally connects to a [fleet of nodes](fleets.status.im) to provide end-to-end p2p chat capabilities.
+Each fleet is a publicly accessible network of Waku v2 peers, providing a bootstrap connection point for new peers, historical message storage, etc.
+The Waku team is currently using this application on the _sandbox_ fleet for internal testing.
+For more information on the available fleets, see [`Connecting to a Waku v2 fleet`](#connecting-to-a-waku-v2-fleet).
+If you want to try our protocols, or join the dogfooding fun, follow the instructions below.
+
+## Preparation
+
+Ensure you have cloned the `nim-waku` repository and installed all prerequisites as per [these instructions](https://github.com/status-im/nim-waku).
+
+Make the `chat2` target.
+
+```
+make chat2
+```
+
+## Basic application usage
+
+To start the `chat2` application in its most basic form, run the following from the project directory
+
+```
+./build/chat2
+```
+
+You should be prompted to provide a nickname for the chat session.
+
+```
+Choose a nickname >>
+```
+
+After entering a nickname, the app will randomly select and connect to a peer from the `sandbox` fleet.
+
+```
+No static peers configured. Choosing one at random from sandbox fleet...
+```
+
+It will then attempt to download historical messages from a random peer in the `sandbox` fleet.
+
+```
+Store enabled, but no store nodes configured. Choosing one at random from sandbox fleet...
+```
+
+Wait for the chat prompt (`>>`) and chat away!
+
+To gracefully exit the `chat2` application, use the `/exit` [in-chat option](#in-chat-options)
+
+```
+>> /exit
+quitting...
+```
+
+## Retrieving historical messages
+
+The `chat2` application can retrieve historical chat messages from a node supporting and running the [Waku v2 store protocol](https://rfc.vac.dev/spec/13/), and will attempt to do so by default.
+It's possible to query a *specific* store node by configuring its `multiaddr` as `storenode` when starting the app:
+
+```
+./build/chat2 --storenode:/dns4/node-01.do-ams3.waku.test.status.im/tcp/30303/p2p/16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W
+```
+
+Alternatively, the `chat2` application will select a random `storenode` for you from the configured fleet (`sandbox` by default) if `storenode` is left unspecified.
+
+```
+./build/chat2
+```
+
+To disable historical message retrieval, use the `--store:false` option:
+
+```
+./build/chat2 --store:false
+```
+
+## Specifying a static peer
+
+In order to connect to a *specific* node as [`relay`](https://rfc.vac.dev/spec/11/) peer, define that node's `multiaddr` as a `staticnode` when starting the app:
+
+```
+./build/chat2 --staticnode:/dns4/node-01.do-ams3.waku.test.status.im/tcp/30303/p2p/16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W
+```
+
+This will bypass the random peer selection process and connect to the specified node.
+
+## Connecting to a Waku v2 fleet
+
+It is possible to specify a specific Waku v2 fleet to connect to when starting the app by using the `--fleet` option:
+
+```
+./build/chat2 --fleet:test
+```
+
+There are currently two fleets to select from, namely _sandbox_ (`waku.sandbox`) and _test_ (`waku.test`).
+The `test` fleet is updated with each incremental change to the `nim-waku` codebase.
+As a result it may have more advanced and experimental features, but will be less stable than `sandbox`.
+The `sandbox` fleet is a deployed network of the latest released Waku v2 nodes.
+If no `fleet` is specified, `chat2` will connect to the `sandbox` fleet by default.
+To start `chat2` without connecting to a fleet, use the `--fleet:none` option _or_ [specify a static peer](#specifying-a-static-peer).
+
+## Specifying a content topic
+
+To publish chat messages on a specific [content topic](https://rfc.vac.dev/spec/14/#wakumessage), use the `--content-topic` option:
+
+```
+./build/chat2 --content-topic:/waku/2/my-content-topic/proto
+```
+
+> **NOTE:** Currently (2021/05/26) the content topic defaults to `/waku/2/huilong/proto` if left unspecified, where `huilong` is the name of our latest testnet.
+
+## In-chat options
+
+| Command | Effect |
+| --- | --- |
+| `/help` | displays available in-chat commands |
+| `/connect` | interactively connect to a new peer |
+| `/nick` | change nickname for current chat session |
+| `/exit` | exits the current chat session |
+
+## `chat2` message protobuf format
+
+Each `chat2` message is encoded as follows
+
+```protobuf
+message Chat2Message {
+ uint64 timestamp = 1;
+ string nick = 2;
+ bytes payload = 3;
+}
+```
+
+where `timestamp` is the Unix timestamp of the message, `nick` is the relevant `chat2` user's selected nickname and `payload` is the actual chat message being sent.
+The `payload` is the byte array representation of a UTF8 encoded string.
+
+# Bridge messages between `chat2` and matterbridge
+
+To facilitate `chat2` use in a variety of contexts, a `chat2bridge` can be deployed to bridge messages between `chat2` and any protocol supported by matterbridge.
+
+## Configure and run matterbridge
+
+1. Download and install [matterbridge](https://github.com/42wim/matterbridge) and configure an instance for the protocol(s) you want to bridge to.
+Basic configuration instructions [here](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config)
+2. Configure the matterbridge API.
+This is used by the `chat2bridge` to relay `chat2` messages to and from matterbridge.
+Configuration instructions for the matterbridge API can be found [here](https://github.com/42wim/matterbridge/wiki/Api).
+The full matterbridge API specification can be found [here](https://app.swaggerhub.com/apis-docs/matterbridge/matterbridge-api/0.1.0-oas3).
+The template below shows an example of a `matterbridge.toml` configuration file for bridging `chat2` to Discord.
+Follow the matterbridge [Discord instructions](https://github.com/42wim/matterbridge/wiki/Section-Discord-%28basic%29) to configure your own `Token` and `Server`.
+```toml
+[discord.mydiscord]
+
+# You can get your token by following the instructions on
+# https://github.com/42wim/matterbridge/wiki/Discord-bot-setup.
+# If you want roles/groups mentions to be shown with names instead of ID,
+# you'll need to give your bot the "Manage Roles" permission.
+Token="MTk4NjIyNDgzNDcdOTI1MjQ4.Cl2FMZ.ZnCjm1XVW7vRze4b7Cq4se7kKWs-abD"
+
+Server="myserver" # picked from guilds the bot is connected to
+
+RemoteNickFormat="{NICK}@chat2: "
+
+[api.myapi]
+BindAddress="127.0.0.1:4242"
+Buffer=1000
+RemoteNickFormat="{NICK}@{PROTOCOL}"
+
+[[gateway]]
+name="gateway1"
+enable=true
+
+[[gateway.inout]]
+account="discord.mydiscord"
+channel="general"
+
+[[gateway.inout]]
+account="api.myapi"
+channel="api"
+```
+3. Run matterbridge using the configuration file created in the previous step.
+Note the API listening address and port in the matterbridge logs (configured as the `BindAddress` in the previous step).
+```
+./matterbridge -conf matterbridge.toml
+```
+```
+[0000] INFO api: Listening on 127.0.0.1:4242
+```
+## Configure and run `chat2bridge`
+1. From the `nim-waku` project directory, make the `chat2bridge` target
+```
+make chat2bridge
+```
+2. Run `chat2bridge` with the following configuration options:
+```
+--mb-host-address Listening address of the Matterbridge host
+--mb-host-port Listening port of the Matterbridge host
+--mb-gateway Matterbridge gateway
+```
+```
+./build/chat2bridge --mb-host-address=127.0.0.1 --mb-host-port=4242 --mb-gateway="gateway1"
+```
+Note that `chat2bridge` encompasses a full `wakunode2` which can be configured with the normal configuration parameters.
+For a full list of configuration options, run `--help`.
+```
+./build/chat2bridge --help
+```
+## Connect `chat2bridge` to a `chat2` network
+1. To bridge messages on an existing `chat2` network, connect to any relay peer(s) in that network from `chat2bridge`.
+This can be done by either specifying the peer(s) as a `--staticnode` when starting the `chat2bridge` or calling the [`post_waku_v2_admin_v1_peers`](https://rfc.vac.dev/spec/16/#post_waku_v2_admin_v1_peers) method on the JSON-RPC API.
+Note that the latter requires the `chat2bridge` to be run with `--rpc=true` and `--rpc-admin=true`.
+1. To bridge from a new `chat2` instance, simply specify the `chat2bridge` listening address as a `chat2` [static peer](#Specifying-a-static-peer).
diff --git a/third-party/nwaku/docs/tutorial/db-migration.md b/third-party/nwaku/docs/tutorial/db-migration.md
new file mode 100644
index 0000000..905d5d1
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/db-migration.md
@@ -0,0 +1,61 @@
+# Database Migration
+This tutorial explains the database migration process in nim-waku.
+
+# Contributors Guide
+## Database Migration Flow
+Nim-waku utilizes the built-in `user_version` variable that Sqlite provides for tracking the database versions.
+The [user_version](https://github.com/waku-org/nwaku/blob/master/waku/waku_archive/driver/sqlite_driver/migrations.nim) MUST be bumped up for every update on the database e.g, table schema/title change.
+Each update should be accompanied by a migration script to move the content of the old version of the database to the new version.
+The script MUST be added to the respective folder as explained in [Migration Folder Structure](#migration-folder-structure) with the proper naming as given in [ Migration Script Naming](#migration-file-naming).
+
+The migration is invoked whenever the database `user_version` is behind the target [user_version](https://github.com/waku-org/nwaku/blob/master/waku/waku_archive/driver/sqlite_driver/migrations.nim) indicated in the nim-waku application.
+The respective migration scripts located in the [migrations folder](https://github.com/waku-org/nwaku/tree/master/migrations) will be executed to upgrade the database from its old version to the target version.
+
+## Migration Folder Structure
+The [migrations folder](https://github.com/waku-org/nwaku/tree/master/migrations) is structured as below.
+
+```
+migrations/
+├── message_store
+│ ├── 00001_addMessageTable.up.sql
+│ ├── 00002_addSenderTimeStamp.up.sql
+│ ├── ...
+└── peer_store
+ └── 00001_addPeerTable.up.sql
+```
+
+
+
+The migration scripts are managed in two separate folders `message_store` and `peer_store`.
+The `message_store` folder contains the migration scripts related to the message store tables. Similarly, the `peer_store` folder contains the scripts relevant to the peer store tables.
+
+
+## Migration File Naming
+The files in [migrations folder](https://github.com/waku-org/nwaku/tree/master/migrations) MUST follow the following naming style in order to be properly included in the migration process.
+Files with invalid naming will be eliminated from the migration process.
+
+`_..sql`
+
+- `version number`: This number should match the target value of `user_version`.
+- `migration script description`: A short description of what the migration script does.
+- `up|down`: One of the keywords of `up` or `down` should be selected.
+ `up` stands for upgrade and `down` means downgrade.
+
+### Example
+A migration file with the name `00002_addTableX.up.sql` should be interpreted as:
+- `00002`: The targeted `user_version` number.
+- `addTableX`: What the script does.
+- `up`: This script `upgrade`s the database from `user_version = 00001` to the `user_version = 00002`.
+
+A downgrade migration file corresponding to the `00002_addTableX.up.sql` can be e.g., `00001_removeTableX.down.sql` and should be interpreted as:
+- `00001`: The targeted `user_version` number.
+- `removeTableX`: What the script does.
+- `down`: This script `downgrade`s the database from `user_version = 00002` to the `user_version = 00001`.
+
+There can be more than one migration file for the same `user_version`.
+The migration process will consider all such files while upgrading/downgrading the database.
+Note that currently we **DO NOT** support **down migration**.
+
+# User Guide
+Migrations work out of the box.
+However, if you want to be extra sure, please take a backup of the SQLite database prior to upgrading your nim-waku version since we currently don't support downgrades of DB.
diff --git a/third-party/nwaku/docs/tutorial/dingpu.md b/third-party/nwaku/docs/tutorial/dingpu.md
new file mode 100644
index 0000000..0d21978
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/dingpu.md
@@ -0,0 +1,44 @@
+# Dingpu testnet
+
+> TODO (2023-05-24): Deprecate or fix
+
+*NOTE: Some of these addresses might change. To get the latest, please see `curl -s https://fleets.status.im | jq '.fleets["waku.test"]'`*
+
+## Basic chat usage
+
+> If historical messaging is desired, the chat app requires that the remote peer specified in `storenode` option supports the WakuStore protocol. For the current cluster node deployed as part of Dingpu this is already the case.
+
+Start two chat apps:
+
+```
+./build/chat2 --ports-shift:0 --storenode:/ip4/178.128.141.171/tcp/60000/p2p/16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W --staticnode:/ip4/178.128.141.171/tcp/60000/p2p/16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W
+./build/chat2 --ports-shift:1 --storenode:/ip4/178.128.141.171/tcp/60000/p2p/16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W --staticnode:/ip4/178.128.141.171/tcp/60000/p2p/16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W
+```
+
+By specifying `staticnode` it connects to that node subscribes to the `waku` topic. This ensures messages are relayed properly.
+
+Then type messages to publish.
+
+## Interactively add a node
+
+There is also an interactive mode. Type `/connect` then paste address of other node. However, this currently has some timing issues with mesh not being updated, so it is advised not to use this until this has been addressed. See https://github.com/waku-org/nwaku/issues/231
+
+## Dingpu cluster node
+
+> TODO (2024-03-11): Fix node multiaddr
+
+## Run a node
+
+To just run a node and not interact on the chat it is enough to run `wakunode2`:
+```
+./build/wakunode2 --staticnode:
+```
+
+You can also run the `wakubridge` process, which runs both a Waku v1 and Waku v2
+node. Currently, it has the same effect as running a `wakunode` and `wakunode2`
+process separately, but bridging functionality will be added later to this
+application.
+
+```
+./build/wakubridge --staticnodev2: --fleetv1:test
+```
diff --git a/third-party/nwaku/docs/tutorial/dns-disc.md b/third-party/nwaku/docs/tutorial/dns-disc.md
new file mode 100644
index 0000000..c9cc24e
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/dns-disc.md
@@ -0,0 +1,69 @@
+# Waku v2 DNS-based Discovery Basic Tutorial
+
+## Background
+
+Waku v2 DNS discovery is a method by which a node may find other peers by retrieving an encoded node list via DNS.
+To achieve this, Waku v2 uses a Nim implementation of [EIP-1459](https://eips.ethereum.org/EIPS/eip-1459).
+According to EIP-1459, the peer list is encoded as a [Merkle tree](https://www.wikiwand.com/en/Merkle_tree) of TXT records.
+Connectivity information for each peer, including [wire address](https://docs.libp2p.io/concepts/addressing/) and [peer ID](https://docs.libp2p.io/concepts/peer-id/), are encapsulated in signed [Ethereum Node Records (ENR)](https://eips.ethereum.org/EIPS/eip-778).
+
+## Mapping ENR to `multiaddr`
+
+EIP-1459 DNS discovery is a scheme for retrieving an ENR list via DNS.
+Waku v2 addressing is based on [libp2p addressing](https://docs.libp2p.io/concepts/addressing/), which uses a `multiaddr` scheme.
+
+The ENR is constructed according to [EIP-778](https://eips.ethereum.org/EIPS/eip-778).
+It maps to the equivalent `libp2p` `multiaddr` for the Waku v2 node as follows:
+
+| ENR Key | ENR Value |
+|-------------|------------------------------------------------------------------------|
+| `id` | name of identity scheme. For Waku v2 generally `v4` |
+| `secp256k1` | the compressed `secp256k1` public key belong to the libp2p peer ID as per [specification](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#keys). This is used to construct the `/p2p/` portion of the node's `multiaddr` |
+| ip | IPv4 address. Corresponds to `/ip4/` portion of the node's `multiaddr` |
+| tcp | TCP port. Corresponds to `/tcp/` portion of the node's `multiaddr` |
+
+The `nim-waku` implementation with integrated DNS discovery already takes care of the ENR to `multiaddr` conversion.
+
+## Usage
+
+Ensure you have built [`wakunode2`](https://github.com/status-im/nim-waku) or [`chat2`](./chat2.md) as per the linked instructions.
+
+The following command line options are available for both `wakunode2` or `chat2`.
+
+```
+--dns-discovery Enable DNS Discovery
+--dns-discovery-url URL for DNS node list in format 'enrtree://@'
+```
+
+- `--dns-discovery` is used to enable DNS discovery on the node. Waku DNS discovery is disabled by default.
+- `--dns-discovery-url` is mandatory if DNS discovery is enabled. It contains the URL for the node list. The URL must be in the format `enrtree://@` where `` is the fully qualified domain name and `` is the base32 encoding of the compressed 32-byte public key that signed the list at that location. See [EIP-1459](https://eips.ethereum.org/EIPS/eip-1459#specification) or the example below to illustrate.
+
+A node will attempt connection to all discovered nodes.
+
+## Example for `waku.test` fleet
+
+To illustrate the above and prove the concept,
+a list of `waku.test` fleet nodes was encoded according to EIP-1459 and deployed to `test.waku.nodes.status.im`.
+The list was signed by the public key `AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI`.
+The complete URL for DNS discovery is therefore: `enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im`.
+
+To run a `wakunode2` with DNS-based discovery of `waku.test` nodes:
+
+```
+./build/wakunode2 --dns-discovery:true --dns-discovery-url:enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im
+```
+
+Similarly, for `chat2`:
+
+```
+./build/chat2 --dns-discovery:true --dns-discovery-url:enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im
+```
+
+The node will discover and attempt connection to all `waku.test` nodes during setup procedures.
+
+To use specific DNS name servers, one or more `--dns-addrs-name-server` arguments can be added:
+
+```
+./build/wakunode2 --dns-discovery:true --dns-discovery-url:enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im --dns-dis
+covery-name-server:8.8.8.8 --dns-addrs-name-server:8.8.4.4
+```
diff --git a/third-party/nwaku/docs/tutorial/filter.md b/third-party/nwaku/docs/tutorial/filter.md
new file mode 100644
index 0000000..2f1a423
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/filter.md
@@ -0,0 +1,35 @@
+# Running Filter Protocol
+
+> TODO (2023-05-24): Deprecate or fix
+
+## How to
+
+Build:
+
+```
+# make wakunode2 is run as part of scripts2 target
+make scripts2
+```
+
+Run two nodes and connect them:
+
+```
+# Starts listening on 60000 with RPC server on 8545.
+# Note the "listening on address" in logs.
+./build/wakunode2 --ports-shift:0
+
+# Run another node with staticnode argument
+./build/wakunode2 --ports-shift:1 --staticnode:/ip4/0.0.0.0/tcp/60000/p2p/16Uiu2HAmF4tuht6fmna6uDqoSMgFqhUrdaVR6VQRyGr6sCpfS2jp --filternode:/ip4/0.0.0.0/tcp/60000/p2p/16Uiu2HAmF4tuht6fmna6uDqoSMgFqhUrdaVR6VQRyGr6sCpfS2jp
+```
+
+You should see your nodes connecting.
+
+Do basic RPC calls:
+
+```
+./build/rpc_subscribe 8545
+./build/rpc_subscribe_filter 8546 # enter your content topic; default is "1"
+./build/rpc_publish 8545 # enter your message in STDIN
+```
+
+You should see other node receive something.
diff --git a/third-party/nwaku/docs/tutorial/heaptrack.md b/third-party/nwaku/docs/tutorial/heaptrack.md
new file mode 100644
index 0000000..d651489
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/heaptrack.md
@@ -0,0 +1,114 @@
+# Heaptrack in Nim Waku
+
+## Background
+Given that RAM is a limited resource, it is crucial to have a good insight on what is going on with the memory used by a particular process.
+
+## Heaptrack
+Heaptrack is a tool that allows to generate memory usage reports.
+It operates in two modes:
+- preload: the tracking is made from the beginning.
+- inject: the tracking starts at a certain time by attaching to a running process.
+
+### Building Heaptrack (tried on Ubuntu)
+- `git clone git@github.com:KDE/heaptrack.git`
+- `mkdir build; cd build`
+- `cmake ..`
+ At this point, make sure the cmake doesn't complain about any missing dependency.
+ Among others, the most tricky deps are obtained by the next commands:
+ - `sudo apt install libkf5i18n-dev`
+ - `sudo apt install libkf5itemmodels-dev`
+ - `sudo apt install libkf5threadweaver-dev`
+ - `sudo apt install libkf5service-dev`
+ - `sudo apt install libkf5completion-dev`
+ - `sudo apt install libkf5itemviews-dev`
+ - `sudo apt install libkf5jobwidgets-dev`
+ - `sudo apt install libkf5solid-dev`
+ - `sudo apt install libkf5coreaddons-dev`
+ - `sudo apt install libkf5auth-dev`
+ - `sudo apt install libkf5codecs-dev`
+ - `sudo apt install libkf5configwidgets-dev`
+ - `sudo apt install libkf5xmlgui-dev`
+ - `sudo apt install libkf5widgetsaddons-dev`
+ - `sudo apt install libqt5gui5`
+ - `sudo apt install libkf5kio-dev`
+ - `sudo apt install libkf5iconthemes-dev`
+- `make`
+- On completion, the `bin/heaptrack_gui` and `bin/heaptrack` binaries will be generated.
+ - heaptrack: needed to generate the report.
+ - heaptrack_gui: needed to analyse the report.
+
+## Heaptrack & Nwaku
+nwaku supports heaptrack, but it needs a special compilation setting.
+
+### Patch Nim compiler to register allocations on Heaptrack
+
+Currently, we rely on the official Nim repository. So we need to patch the Nim compiler to register allocations and deallocations on Heaptrack.
+For Nim 2.2.4 version, we created a patch that can be applied as:
+```bash
+git apply --directory=vendor/nimbus-build-system/vendor/Nim docs/tutorial/nim.2.2.4_heaptracker_addon.patch
+git add .
+git commit -m "Add heaptrack support to Nim compiler - temporary patch"
+```
+
+> Until heaptrack support is not available in official Nim, so it is important to keep it in the `nimbus-build-system` repository.
+> Commit ensures that `make update` will not override the patch unintentionally.
+
+> We are planning to make it available through an official PR for Nim.
+
+When the patch is applied, we can build wakunode2 with heaptrack support.
+
+### Build nwaku with heaptrack support
+
+`make -j HEAPTRACKER=1 wakunode2`
+
+### Create nwaku memory report with heaptrack
+
+nwaku only works correctly with heaptrack operating in 'preload' mode, i.e. the memory is tracked from the beginning.
+To achieve this, the `heaptrack` binary should be prepended to the usual `wakunode`:
+
+e.g.:
+`/build/bin/heaptrack /build/wakunode2 ...`
+
+While the above is running, a file with the next format is being populated with allocs/deallocs stats in the current folder:
+
+ ```
+ heaptrack...gz
+e.g.:
+ heaptrack.wakunode2.23125.gz
+ ```
+
+### Build a Docker image with Heaptrack + Nim Waku
+Having Docker properly installed in your machine, do the next:
+
+- cd to the `nwaku` root folder.
+- ```sudo make docker-image DOCKER_IMAGE_NAME=docker_repo:docker_tag HEAPTRACKER=1```
+- alternatively you can use the `docker-quick-image` target, this is faster but creates an ubuntu based image, so your local build environment must match.
+
+That will create a Docker image with both nwaku and heaptrack. The container's entry point is `ENTRYPOINT ["/heaptrack/build/bin/heaptrack", "/usr/bin/wakunode"]`, so the memory report starts being generated from the beginning.
+
+#### Notice for using heaptrack supporting image with `docker compose`
+
+Take care that wakunode2 should be started as
+```
+exec /heaptrack/build/bin/heaptrack /usr/bin/wakunode\
+... all the arguments you want to pass to wakunode ...
+```
+
+### Extract report file from a running Docker container
+Bear in mind that if you restart the container, the previous report will get lost. Therefore, before restarting, it is important to extract it from the container once you consider it has enough information.
+
+While the Docker container is running, run the next command:
+```
+sudo docker cp 768e7de52d3c:/heaptrack.wakunode.1.gz .
+```
+(replace the 768.. with your docker container id.).
+
+### Analyse a heaptrack report
+```
+/bin/heaptrack_gui heaptrack.wakunode.1.gz
+```
+
+You should be able to see memory allocations. It is important
+to see a legend like shown below:
+
+
diff --git a/third-party/nwaku/docs/tutorial/imgs/good_heaptrack_report_example.png b/third-party/nwaku/docs/tutorial/imgs/good_heaptrack_report_example.png
new file mode 100644
index 0000000..088ef51
Binary files /dev/null and b/third-party/nwaku/docs/tutorial/imgs/good_heaptrack_report_example.png differ
diff --git a/third-party/nwaku/docs/tutorial/imgs/infura-dashboard-mainnet.png b/third-party/nwaku/docs/tutorial/imgs/infura-dashboard-mainnet.png
new file mode 100644
index 0000000..c5cf2df
Binary files /dev/null and b/third-party/nwaku/docs/tutorial/imgs/infura-dashboard-mainnet.png differ
diff --git a/third-party/nwaku/docs/tutorial/imgs/infura-dashboard.png b/third-party/nwaku/docs/tutorial/imgs/infura-dashboard.png
new file mode 100644
index 0000000..5b2a846
Binary files /dev/null and b/third-party/nwaku/docs/tutorial/imgs/infura-dashboard.png differ
diff --git a/third-party/nwaku/docs/tutorial/imgs/infura-endpoints.png b/third-party/nwaku/docs/tutorial/imgs/infura-endpoints.png
new file mode 100644
index 0000000..86ce254
Binary files /dev/null and b/third-party/nwaku/docs/tutorial/imgs/infura-endpoints.png differ
diff --git a/third-party/nwaku/docs/tutorial/imgs/infura-key.png b/third-party/nwaku/docs/tutorial/imgs/infura-key.png
new file mode 100644
index 0000000..020ca0c
Binary files /dev/null and b/third-party/nwaku/docs/tutorial/imgs/infura-key.png differ
diff --git a/third-party/nwaku/docs/tutorial/imgs/rln-relay-chat2-overview.png b/third-party/nwaku/docs/tutorial/imgs/rln-relay-chat2-overview.png
new file mode 100644
index 0000000..5208963
Binary files /dev/null and b/third-party/nwaku/docs/tutorial/imgs/rln-relay-chat2-overview.png differ
diff --git a/third-party/nwaku/docs/tutorial/nangang.md b/third-party/nwaku/docs/tutorial/nangang.md
new file mode 100644
index 0000000..1c40701
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/nangang.md
@@ -0,0 +1,42 @@
+# Nangang Test
+
+> TODO (2023-05-24): Deprecate or fix
+
+Nangang is the first internal testnet. See
+https://github.com/vacp2p/research/issues/43 for more.
+
+## How to
+
+Build:
+
+```
+# make wakunode2 is run as part of scripts2 target
+make scripts2
+```
+
+Run two nodes and connect them:
+
+```
+# Starts listening on 60000 with RPC server on 8545.
+# Note the "listening on address" in logs.
+./build/wakunode2 --ports-shift:0
+
+# Run another node with staticnode argument
+./build/wakunode2 --ports-shift:1 --staticnode:/ip4/0.0.0.0/tcp/60000/p2p/16Uiu2HAmF4tuht6fmna6uDqoSMgFqhUrdaVR6VQRyGr6sCpfS2jp
+```
+
+You should see your nodes connecting.
+
+Do basic RPC calls:
+
+```
+./build/rpc_subscribe 8545
+./build/rpc_subscribe 8546
+./build/rpc_publish 8545 # enter your message in STDIN
+```
+
+You should see other node receive something.
+
+## Nangang cluster node
+
+> TODO (2024-03-11): Fix node multiaddr
diff --git a/third-party/nwaku/docs/tutorial/nim.2.2.4_heaptracker_addon.patch b/third-party/nwaku/docs/tutorial/nim.2.2.4_heaptracker_addon.patch
new file mode 100644
index 0000000..3cd3384
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/nim.2.2.4_heaptracker_addon.patch
@@ -0,0 +1,44 @@
+diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
+index e2dd43075..7f8c8e04e 100644
+--- a/lib/system/alloc.nim
++++ b/lib/system/alloc.nim
+@@ -1,4 +1,4 @@
+-#
++#!fmt: off
+ #
+ # Nim's Runtime Library
+ # (c) Copyright 2012 Andreas Rumpf
+@@ -862,6 +862,15 @@ when defined(gcDestructors):
+ dec maxIters
+ if it == nil: break
+
++when defined(heaptracker):
++ const heaptrackLib =
++ when defined(heaptracker_inject):
++ "libheaptrack_inject.so"
++ else:
++ "libheaptrack_preload.so"
++ proc heaptrack_malloc(a: pointer, size: int) {.cdecl, importc, dynlib: heaptrackLib.}
++ proc heaptrack_free(a: pointer) {.cdecl, importc, dynlib: heaptrackLib.}
++
+ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
+ when defined(nimTypeNames):
+ inc(a.allocCounter)
+@@ -984,6 +993,8 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
+ sysAssert(isAccessible(a, result), "rawAlloc 14")
+ sysAssert(allocInv(a), "rawAlloc: end")
+ when logAlloc: cprintf("var pointer_%p = alloc(%ld) # %p\n", result, requestedSize, addr a)
++ when defined(heaptracker):
++ heaptrack_malloc(result, requestedSize)
+
+ proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
+ result = rawAlloc(a, requestedSize)
+@@ -992,6 +1003,8 @@ proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
+ proc rawDealloc(a: var MemRegion, p: pointer) =
+ when defined(nimTypeNames):
+ inc(a.deallocCounter)
++ when defined(heaptracker):
++ heaptrack_free(p)
+ #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
+ sysAssert(allocInv(a), "rawDealloc: begin")
+ var c = pageAddr(p)
diff --git a/third-party/nwaku/docs/tutorial/onchain-rln-relay-chat2.md b/third-party/nwaku/docs/tutorial/onchain-rln-relay-chat2.md
new file mode 100644
index 0000000..ac2bdc3
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/onchain-rln-relay-chat2.md
@@ -0,0 +1,232 @@
+# Spam-protected chat2 application with on-chain group management
+
+This document is a tutorial on how to run the chat2 application in the spam-protected mode using the Waku-RLN-Relay protocol and with dynamic/on-chain group management.
+In the on-chain/dynamic group management, the state of the group members i.e., their identity commitment keys is moderated via a membership smart contract deployed on the Linea Sepolia network which is one of the test-nets.
+Members can be dynamically added to the group and the group size can grow up to 2^20 members.
+This differs from the prior test scenarios in which the RLN group was static and the set of members' keys was hardcoded and fixed.
+
+
+## Prerequisites
+To complete this tutorial, you will need
+
+1. An rln keystore file with credentials to the rln membership smart contract you wish to use. You may obtain this by registering to the smart contract and generating a keystore, or by using the [rln-keystore-generator](./rln-keystore-generator.md) which does that for you.
+
+
+## Overview
+Figure 1 provides an overview of the interaction of the chat2 clients with the test fleets and the membership contract.
+At a high level, when a chat2 client is run with Waku-RLN-Relay mounted in on-chain mode.
+
+Under the hood, the chat2 client constantly listens to the membership contract and keeps itself updated with the latest state of the group.
+
+In the following test setting, the chat2 clients are to be connected to the Waku test fleets as their first hop.
+The test fleets will act as routers and are also set to run Waku-RLN-Relay over the same pubsub topic and content topic as chat2 clients i.e., the default pubsub topic of `/waku/2/rs/0/0` and the content topic of `/toy-chat/3/mingde/proto`.
+Spam messages published on the said combination of topics will be caught by the test fleet nodes and will not be routed.
+Note that spam protection does not rely on the presence of the test fleets.
+In fact, all the chat2 clients are also capable of catching and dropping spam messages if they receive any.
+You can test it by connecting two chat2 clients (running Waku-RLN-Relay) directly to each other and see if they can spot each other's spam activities.
+
+ 
+ Figure 1.
+
+# Set up
+## Build chat2
+First, build chat2
+
+```bash
+make chat2
+```
+
+## Set up a chat2 client
+
+Run the following command to set up your chat2 client.
+
+```bash
+./build/chat2 --fleet:test \
+--content-topic:/toy-chat/3/mingde/proto \
+--rln-relay:true \
+--rln-relay-dynamic:true \
+--rln-relay-eth-contract-address:0xB9cd878C90E49F797B4431fBF4fb333108CB90e6 \
+--rln-relay-cred-path:xxx/xx/rlnKeystore.json \
+--rln-relay-cred-password:xxxx \
+--rln-relay-eth-client-address:xxxx \
+--ports-shift:1
+```
+
+In this command
+- the `--fleet:test` indicates that the chat2 app gets connected to the test fleets.
+- the `toy-chat/3/mingde/proto` passed to the `content-topic` option indicates the content topic on which the chat2 application is going to run.
+- the `rln-relay` flag is set to `true` to enable the Waku-RLN-Relay protocol for spam protection.
+- the `--rln-relay-dynamic` flag is set to `true` to enable the on-chain mode of Waku-RLN-Relay protocol with dynamic group management.
+- the `--rln-relay-eth-contract-address` option gets the address of the membership contract.
+ The current address of the contract is `0xB9cd878C90E49F797B4431fBF4fb333108CB90e6`.
+ You may check the state of the contract on the [Linea Sepolia testnet](https://sepolia.lineascan.build/address/0xB9cd878C90E49F797B4431fBF4fb333108CB90e6).
+- the `--rln-relay-cred-path` option denotes the path to the keystore file described above
+- the `--rln-relay-cred-password` option denotes the password to the keystore
+- the `rln-relay-eth-client-address` is the WebSocket address of the hosted node on the Linea Sepolia testnet.
+ You need to replace the `xxxx` with the actual node's address.
+
+For `rln-relay-eth-client-address`, if you do not know how to obtain it, you may use the following tutorial on the [prerequisites of running on-chain spam-protected chat2](./pre-requisites-of-running-on-chain-spam-protected-chat2.md).
+
+You may set up more than one chat client,
+just make sure that you increment the `--ports-shift` value for each new client you set up e.g., `--ports-shift=2`.
+
+Once you run the command, you are asked to choose your nickname:
+```
+Choose a nickname >> Alice
+```
+
+then you will see a couple of other messages related to setting up the connections of your chat app,
+the content may differ on your screen though:
+```
+Connecting to test fleet using DNS discovery...
+Discovered and connecting to @[16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp, 16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W, 16Uiu2HAmDCp8XJ9z1ev18zuv8NHekAsjNyezAvmMfFEJkiharitG]
+Listening on
+ /ip4/75.157.120.249/tcp/60001/p2p/16Uiu2HAmQXuZmbjFWGagthwVsPFrc5ZrZ9c53qdUA45TWoZaokQn
+Store enabled, but no store nodes configured. Choosing one at random from discovered peers
+Connecting to storenode: 16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp
+```
+You will also see some historical messages being fetched, again the content may be different on your end:
+
+```
+ Bob: hi
+ Bob: hi
+ Alice: spam1
+ Alice: hiiii
+ Alice: hello
+ Bob: hi
+ Bob: hi
+ Alice: hi
+ b: hi
+ h: hi
+...
+```
+
+Next, you see the following message:
+```
+rln-relay preparation is in progress ...
+```
+Also, the registered RLN identity key, the RLN identity commitment key, and the index of the registered credential will be displayed as given below.
+Note that in the figure, the RLN identity key is not shown for security reasons (replaced by a string of `x`s).
+But, you will see your RLN identity key.
+
+```
+your membership index is: xx
+your RLN identity key is: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+your RLN identity commitment key is: 6c6598126ba10d1b70100893b76d7f8d7343eeb8f5ecfd48371b421c5aa6f012
+```
+
+Finally, the chat prompt `>>` will appear which means your chat2 client is ready.
+Once you type a chat line and hit enter, you will see a message that indicates the epoch at which the message is sent e.g.,
+
+```
+>> Hi
+--rln epoch: 165886530
+ Alice: Hi
+```
+The numerical value `165886530` indicates the epoch of the message `Hi`.
+You will see a different value than `165886530` on your screen.
+If two messages sent by the same chat2 client happen to have the same RLN epoch value, then one of them will be detected as spam and won't be routed (by test fleets in this test setting).
+At the time of this tutorial, the epoch duration is set to `10` seconds.
+You can inspect the current epoch value by checking the following [constant variable](https://github.com/waku-org/nwaku/blob/44c543129ee4149255a00a05f1e7d21f8fa28626/waku/v2/waku_rln_relay/constants.nim#L51) in the nim-waku codebase.
+Thus, if you send two messages less than `10` seconds apart, they are likely to get the same `rln epoch` values.
+
+After sending a chat message, you may experience some delay before the next chat prompt appears.
+The reason is that under the hood a zero-knowledge proof is being generated and attached to your message.
+
+
+Try to spam the network by violating the message rate limit i.e.,
+sending more than one message per epoch.
+Your messages will be routed via test fleets that are running in spam-protected mode over the same content topic i.e., `/toy-chat/3/mingde/proto` as your chat client.
+Your spam activity will be detected by them and your message will not reach the rest of the chat clients.
+You can check this by running a second chat user and verifying that spam messages are not displayed as they are filtered by the test fleets.
+Furthermore, the chat client will prompt you with the following warning message indicating that the message rate is being violated:
+```
+⚠️ message rate violation! you are spamming the network!
+```
+A sample test scenario is illustrated in the [Sample test output section](#sample-test-output).
+
+Once you are done with the test, make sure you close all the chat2 clients by typing the `/exit` command.
+```
+>> /exit
+quitting...
+```
+
+
+# Sample test output
+In this section, a sample test of running two chat clients is provided.
+Note that the value used for `rln-relay-eth-client-address` in the following code snippets is junk and not valid.
+
+The two chat clients namely `Alice` and `Bob` are connected to the test fleets.
+`Alice` sends 4 messages i.e., `message1`, `message2`, `message3`, and `message4`.
+However, only three of them reach `Bob`.
+This is because the two messages `message2` and `message3` have identical RLN epoch values, so, one of them gets discarded by the test fleets as a spam message.
+The test fleets do not relay `message3` further, hence `Bob` never receives it.
+You can check this fact by looking at `Bob`'s console, where `message3` is missing.
+
+
+**Alice**
+```bash
+./build/chat2 --fleet:test --content-topic:/toy-chat/3/mingde/proto --rln-relay:true --rln-relay-dynamic:true --rln-relay-eth-contract-address:0xB9cd878C90E49F797B4431fBF4fb333108CB90e6 --rln-relay-cred-path:rlnKeystore.json --rln-relay-cred-password:password --rln-relay-eth-client-address:https://sepolia.infura.io/v3/12345678901234567890123456789012 --ports-shift=1
+```
+
+```
+Choose a nickname >> Alice
+Welcome, Alice!
+Connecting to test fleet using DNS discovery...
+Discovered and connecting to @[16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp, 16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W, 16Uiu2HAmDCp8XJ9z1ev18zuv8NHekAsjNyezAvmMfFEJkiharitG]
+Listening on
+ /ip4/75.157.120.249/tcp/60001/p2p/16Uiu2HAmH7XbkcdbA1CCs91r93HuwZHSdXppCNvJTDVvgGhuxyuG
+Store enabled, but no store nodes configured. Choosing one at random from discovered peers
+Connecting to storenode: 16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp
+ Bob: hi
+ Bob: hi
+ Alice: spam1
+ Alice: hiiii
+ Alice: hello
+ Bob: hi
+ Bob: hi
+ Alice: hi
+ b: hi
+ h: hi
+rln-relay preparation is in progress ...
+your membership index is: xx
+your rln identity key is: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+your rln identity commitment key is: bd093cbf14fb933d53f596c33f98b3df83b7e9f7a1906cf4355fac712077cb28
+>> message1
+--rln epoch: 165886591
+ Alice: message1
+>> message2
+--rln epoch: 165886592
+ Alice: message2
+>> message3
+--rln epoch: 165886592 ⚠️ message rate violation! you are spamming the network!
+ Alice: message3
+>> message4
+--rln epoch: 165886593
+ Alice: message4
+>>
+```
+
+**Bob**
+```bash
+./build/chat2 --fleet:test --content-topic:/toy-chat/3/mingde/proto --rln-relay:true --rln-relay-dynamic:true --rln-relay-eth-contract-address:0xB9cd878C90E49F797B4431fBF4fb333108CB90e6 --rln-relay-cred-path:rlnKeystore.json --rln-relay-cred-index:1 --rln-relay-cred-password:password --rln-relay-eth-client-address:https://sepolia.infura.io/v3/12345678901234567890123456789012 --ports-shift=2
+```
+
+```
+Choose a nickname >> Bob
+Welcome, Bob!
+Connecting to test fleet using DNS discovery...
+Discovered and connecting to @[16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp, 16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W, 16Uiu2HAmDCp8XJ9z1ev18zuv8NHekAsjNyezAvmMfFEJkiharitG]
+Listening on
+ /ip4/75.157.120.249/tcp/60002/p2p/16Uiu2HAmE7fPUWGJ7UFJ3p2a3RNiEtEvAWhpfUStcCDmVGhm4h4Z
+Store enabled, but no store nodes configured. Choosing one at random from discovered peers
+Connecting to storenode: 16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp
+rln-relay preparation is in progress ...
+your membership index is: xx
+your rln identity key is: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+your rln identity commitment key is: d4961a7681521730bc7f9ade185c632b94b70624b2e87e21a97c07b83353f306
+>> Alice: message1
+>> Alice: message2
+>> Alice: message4
+>>
+```
diff --git a/third-party/nwaku/docs/tutorial/pre-requisites-of-running-on-chain-spam-protected-chat2.md b/third-party/nwaku/docs/tutorial/pre-requisites-of-running-on-chain-spam-protected-chat2.md
new file mode 100644
index 0000000..f1757ad
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/pre-requisites-of-running-on-chain-spam-protected-chat2.md
@@ -0,0 +1,104 @@
+In this tutorial you will learn how to:
+1. Create a Sepolia Ethereum account and obtain its private key.
+2. Obtain Sepolia ETH from faucet.
+3. Access a node on the Sepolia testnet using Infura.
+
+## 1. Create a Sepolia Ethereum account and obtain its private key
+
+> _**WARNING:**_ The private key is used elsewhere by Waku RLN registration tools to assist with membership registration in the Sepolia test network.
+> We strongly recommend that you create an account only for this purpose.
+> NEVER expose a private key that controls any valuable assets or funds.
+
+1. Download and install Metamask. [https://metamask.io/download/](https://metamask.io/download/)
+ If you already have Metamask installed, go to step 3.
+ If you encounter any issues during the Metamask setup process, please refer to the [official Metamask support page](https://support.metamask.io/hc/en-us).
+2. Create a new wallet and save your secret recovery phrase.
+
+ 
+
+3. Login to Metamask.
+
+ 
+
+4. By default, Metamask connects to the Ethereum Mainnet (dropdown menu in the top right corner).
+
+ 
+
+ To publish messages to the Waku Network, you need to connect to the Sepolia test network.
+5. Switch to the Sepolia test network by selecting it from the dropdown menu. Ensure "Show test networks" is enabled.
+
+ 
+
+ The same account can be used with different networks. Note that the ETH balance is different for each network (each has its own native token).
+
+ 
+
+6. To view the private key for this account, click on the three dots next to the account name and select "Account Details".
+
+ 
+
+ Select "Show Private Key".
+
+ 
+
+ Enter your Metamask password and click "Confirm"
+
+ 
+
+ You will be shown the private key.
+
+## 2. Obtain Sepolia ETH from faucet
+
+Sepolia ETH can be obtained from different faucets.
+Three popular examples include:
+
+ 1. [sepoliafaucet.com](https://sepoliafaucet.com/) (requires an Alchemy account)
+ 2. [Infura Sepolia faucet](https://www.infura.io/faucet/sepolia) (requires an Infura account)
+ 3. [Sepolia POW faucet](https://sepolia-faucet.pk910.de/)
+
+> _**NOTE:**_ This list is provided for convenience. We do not necessarily recommend or guarantee the security of any of these options.
+
+Many faucets limit the amount of Sepolia ETH you can obtain per day.
+We include instructions for [sepolia-faucet.pk910.de](https://sepolia-faucet.pk910.de/) as an example:
+
+1. Enter your Sepolia Ethereum account public address, solve the Captcha and start mining.
+
+ 
+
+2. Keep the browser tab open for a while. You can see the estimated Sepolia ETH mined per hour.
+
+ 
+
+ Each session is limited to a few hours.
+3. When you've mined enough Sepolia ETH (minimum of 0.05 Sepolia ETH), click on "Stop Mining" and claim your reward.
+
+ 
+
+## 3. Access a node on the Sepolia testnet using Infura
+
+> _**NOTE:**_ Infura provides a simple way of setting up endpoints for interaction with the Ethereum chain and the Waku RLN smart contract without having to run a dedicated Ethereum node.
+> Setting up Infura is not mandatory. Operators concerned with the centralized aspect introduced by Infura should use their own node.
+
+1. Sign up for Infura if you do not have an account already. [https://infura.io/register](https://infura.io/register)
+
+ 
+
+ Follow the instructions to register and verify the account.
+
+2. An API Key named "My First Key" should be auto-generated. Click on it, otherwise click on the "Create New API Key" button.
+
+ 
+
+
+3. You will be presented with a dashboard for your new key. Make sure to have Ethereum Sepolia's checkbox selected in the Networks section.
+
+ 
+
+
+4. Select the "Sepolia" endpoint in the Ethereum menu.
+
+ 
+
+ Both Https and WebSockets endpoints are available. Waku requires the Https endpoint.
+
+5. Copy the address (starting with `https://sepolia.infura`) as needed when setting up your Waku node.
diff --git a/third-party/nwaku/docs/tutorial/rln-chat-cross-client.md b/third-party/nwaku/docs/tutorial/rln-chat-cross-client.md
new file mode 100644
index 0000000..837bf89
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/rln-chat-cross-client.md
@@ -0,0 +1,33 @@
+# Waku-RLN-Relay Testnet2: Cross-Client
+
+In this tutorial, the aim is to test the interoperability of the 3 available Waku v2 clients namely, Nim, Go, and JS over the Waku network in the spam-protected mode.
+Spam protection is done by rate-limiting each message publisher.
+At the time of this tutorial, the messaging rate is set to `1` per Epoch where Epoch duration is set to `10` seconds.
+You will find more about the details of spam protection in the chat clients tutorial provided below.
+Messaging rate/spam protection is enabled through [Waku-RLN-Relay protocol](https://rfc.vac.dev/spec/17/) that is mounted on the routing hops.
+For ease of demonstration, we make use of Nim-chat, Go-chat, and JS-chat applications that are developed on top of their respective Waku v2 clients.
+
+You need to set up a chat application in spam-protected mode and then start messaging with it.
+As for the setup, please follow the tutorials below:
+- [Nim-chat](./onchain-rln-relay-chat2.md)
+- [Go-chat](https://github.com/waku-org/go-waku/blob/master/docs/tutorials/rln.md)
+- [JS-chat](https://examples.waku.org/rln-js/)
+
+Once you set up your chat client, it will be connected to the Waku v2 test fleets as its first hop.
+Messages generated by the chat client are set to be published on a specific combination of pubsub and content topic i.e., the default pubsub topic of `/waku/2/rs/0/0` and the content topic of `/toy-chat/3/mingde/proto`.
+The test fleets also run Waku-RLN-Relay over the same pubsub topic and content topic.
+Test fleets act as routers and enforce the message rate limit.
+As such, any spam messages published by a chat client on the said combination of topics will be caught by the Waku v2 test fleet nodes and will not be routed.
+You may also run multiple chat instances from the same or different client implementations to better observe the spam protection done by the Waku v2 test fleets.
+Note that spam protection does not rely on the presence of the test fleets.
+In fact, all the chat clients (except js-chat as it is in progress) are also capable of catching and dropping spam messages if they receive any.
+You can test it by connecting two chat clients (running Waku-RLN-Relay) directly to each other and see if they can spot each other's spam activities.
+
+Note: JS-chat will use the [WAKU2-LIGHTPUSH protocol](https://rfc.vac.dev/spec/19/) to push its messages to the Waku v2 test fleets.
+Waku v2 test fleets will act according to the WAKU2-LIGHTPUSH specifications and push that message to the network without any further verification.
+That is, they do not enforce spam protection in that specific protocol but rather act merely as a message publisher (this behavior may change in the future though).
+As such, you can expect to receive spam messages published by the JS-chat clients from other connecting chat clients i.e., Go-chat and Nim-chat.
+However, you will see that such messages will be immediately identified as spam on those clients and a proper message will be displayed on the console.
+
+
+You can also find a recorded demo of this testnet in the following [video](https://drive.proton.me/urls/EC4G8SY2J8#ie92Wtje1f4O).
\ No newline at end of file
diff --git a/third-party/nwaku/docs/tutorial/rln-chat2-live-testnet.md b/third-party/nwaku/docs/tutorial/rln-chat2-live-testnet.md
new file mode 100644
index 0000000..c874294
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/rln-chat2-live-testnet.md
@@ -0,0 +1,125 @@
+# Communicating with waku2 test fleets using chat2 application in spam-protected mode
+
+This document is a tutorial on how to run chat2 in spam-protected/rate-limited mode using the waku-RLN-Relay protocol on a designated content topic `/toy-chat/3/mingde/proto`.
+You will connect your chat2 client to waku2 test fleets.
+Note that test fleets will not filter spam messages, they merely route messages.
+Spam detection takes place at the chat2 users end.
+In this setting, you should try to spam the network by violating the message rate limit i.e.,
+sending more than one message per epoch.
+At the time of this tutorial, the epoch duration is set to `10` seconds.
+You can inspect the current epoch value by checking the following [constant variable](https://github.com/status-im/nim-waku/blob/21cac6d491a6d995a7a8ba84c85fecc7817b3d8b/waku/v2/protocol/waku_rln_relay/constants.nim#L245) in the nim-waku codebase.
+Your messages will be routed via test fleets and will arrive at other live chat2 clients that are running in rate-limited mode over the same content topic i.e., `/toy-chat/3/mingde/proto`.
+Your spam activity will be detected by them and a proper message will be shown on their console.
+
+# Set up
+## Build chat2
+First, build chat2
+
+```
+make chat2
+```
+
+## Setup a chat2 node in rate-limited mode
+Run the following command to set up your chat2 client.
+
+```
+./build/chat2 --content-topic:/toy-chat/3/mingde/proto --ports-shift=1 --fleet:test --rln-relay:true --rln-relay-membership-index:your_index
+
+```
+In this command
+- the `rln-relay` flag is set to true to enable RLN-Relay protocol for spam protection.
+- the `rln-relay-membership-index` is used to pick one RLN key out of the 100 available hardcoded RLN keys.
+You can pass your index using this command `--rln-relay-membership-index: your_index` e.g., `--rln-relay-membership-index:19` .
+Please use the index assigned to you in the dogfooding coordination phase.
+If you pick an index at random you may end up using the same key-pair as someone else, hence your messaging rate will be shared with that person(s).
+
+
+Next, choose your nickname:
+```
+Choose a nickname >> your_nick_name
+```
+Wait for the chat prompt `>>` to appear.
+Now your chat2 client is ready.
+
+You may set up more than one chat client,
+just make sure that you increment the `--ports-shift` value for each new client you set up e.g., `--ports-shift=2`.
+
+# Run the test
+Now that you have set up your client, start chatting.
+Once you type a chat line and hit enter, you will see a message that indicates the epoch at which the message is sent e.g.,
+```
+>> Hi!
+--rln epoch: 164495684
+ Bob: Hi!
+```
+The numerical value `164495684` indicates the epoch of the message `Hi!`.
+You will see a different value than `164495684` on your screen.
+If two messages sent by the same chat2 client happen to have the same RLN epoch value, then one of them will be detected as spam by the receiving chat2 clients.
+At the time of this tutorial, the epoch duration is set to `10` seconds.
+Thus, if you send two messages less than `10` seconds apart, they are likely to get the same `rln epoch` values.
+
+After sending a chat message, you may experience some delay before the next chat prompt appears.
+The reason is that under the hood a zero-knowledge proof is being generated and attached to your message.
+
+Once you are done with the test, make sure you close all the chat2 clients by typing `/exit` command.
+```
+>> /exit
+quitting...
+```
+
+# Sample test output
+
+In the following sample test, two chat2 clients are set up, namely `Alice` and `Bob`.
+`Bob` sends three messages i.e., `message1`, `message2`, and `message3` to the test fleets.
+Test fleets will route the messages to their connections including `Alice`.
+The two messages `message2` and `message3` have an identical RLN epoch value of `164504930`, so, one of them will be detected as a spam message by `Alice`.
+You can check this fact by looking at the `Alice` console, where `A spam message is found and discarded : Bob: message3` is presented.
+
+
+Bob
+```
+./build/chat2 --content-topic:/toy-chat/3/mingde/proto --ports-shift=2 --fleet:test --rln-relay:true --rln-relay-membership-index:2
+Choose a nickname >> Bob
+Welcome, Bob!
+Connecting to test fleet using DNS discovery...
+Discovered and connecting to @[16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W, 16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp, 16Uiu2HAmDCp8XJ9z1ev18zuv8NHekAsjNyezAvmMfFEJkiharitG]
+Listening on
+ /ip4/75.157.120.249/tcp/60002/p2p/16Uiu2HAmKdCdP89q6CwLc6PeFDJnVR1EmM7fTgtphHiacSNBnuAz
+Store enabled, but no store nodes configured. Choosing one at random from discovered peers
+Connecting to storenode: 16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W
+>> message1
+--rln epoch: 164504929
+ Bob: message1
+>> message2
+--rln epoch: 164504930
+ Bob: message2
+>> message3
+--rln epoch: 164504930
+ Bob: message3
+>> message4
+--rln epoch: 164504973
+ Bob: message4
+>> /exit
+quitting...
+```
+
+
+Alice
+```
+./build/chat2 --content-topic:/toy-chat/3/mingde/proto --ports-shift=1 --fleet:test --rln-relay:true --rln-relay-membership-index:1
+
+Choose a nickname >> Alice
+Welcome, Alice!
+Connecting to test fleet using DNS discovery...
+Discovered and connecting to @[16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W, 16Uiu2HAkzHaTP5JsUwfR9NR8Rj9HC24puS6ocaU8wze4QrXr9iXp, 16Uiu2HAmDCp8XJ9z1ev18zuv8NHekAsjNyezAvmMfFEJkiharitG]
+Listening on
+ /ip4/75.157.120.249/tcp/60001/p2p/16Uiu2HAkyTos6LeGrj1YJyA3WYzp9qKQGCsxbtvyoBRHSu9PCrQZ
+Store enabled, but no store nodes configured. Choosing one at random from discovered peers
+Connecting to storenode: 16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W
+>> Bob: message1
+>> Bob: message2
+>> A spam message is found and discarded : Bob: message3
+ Bob: message4
+>> /exit
+quitting...
+```
diff --git a/third-party/nwaku/docs/tutorial/rln-chat2-local-test.md b/third-party/nwaku/docs/tutorial/rln-chat2-local-test.md
new file mode 100644
index 0000000..f5d4bdb
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/rln-chat2-local-test.md
@@ -0,0 +1,147 @@
+# Building a local network of spam-protected chat2 clients
+
+This document is a tutorial on how to locally set up a small network of chat2 clients in a spam-protected mode using the waku-RLN-Relay protocol.
+In the provided test scenario, you will set up three chat2 clients.
+For ease of explanation, we will refer to them as `Alice`, `Bob`, and `Carol`.
+`Bob` and `Carol` are directly connected to `Alice` so that their message will be routed via `Alice`.
+In this setting, if `Bob` or `Carol` attempts to spam the network by violating the message rate limit then `Alice` will detect their spamming activity, and does not relay the spam messages.
+The message rate is one per epoch.
+At the time of this tutorial, the epoch duration is set to `10` seconds.
+You can inspect its current value by checking the following [constant variable](https://github.com/waku-org/nwaku/blob/44c543129ee4149255a00a05f1e7d21f8fa28626/waku/v2/waku_rln_relay/constants.nim#L51) in the nim-waku codebase.
+
+
+# Set up
+## Build chat2
+First, build chat2
+
+```
+make chat2
+```
+
+## Create a local network of chat2 clients
+Next, set up the following three chat2 clients in order.
+As `Alice` is going to be the only connection point between `Bob` and `Carol`, you need to set it up first before `Bob` and `Carol`.
+
+**Alice setup**:
+Run the following command to set up the first chat2 client. In this command, the `rln-relay` flag is set to true to enable RLN-Relay protocol for the spam protection.
+The `rln-relay-membership-index` is used to pick one RLN key out of the 100 available hardcoded RLN keys.
+We use the first RLN key of the list for `Alice` i.e., `--rln-relay-membership-index:1`.
+
+```
+./build/chat2 --staticnode:/ip4/127.0.0.1/tcp/60010/p2p/16Uiu2HAmKdCdP89q6CwLc6PeFDJnVR1EmM7fTgtphHiacSNBnuAz --content-topic:/toy-chat/3/mingde/proto --ports-shift=1 --fleet:none --nodekey=f157b19b13e9ee818acfc9d3d7eec6b81f70c0a978dec19def261172acbe26e6 --rln-relay:true --rln-relay-membership-index:1
+
+```
+
+Next, you will be prompted with a message to choose a nickname, set it to `Alice`:
+```
+Choose a nickname >> Alice
+```
+Wait for the chat prompt `>>` to appear.
+Now your first chat2 client is ready.
+
+
+
+
+**Bob setup**:
+Set up the second chat2 client using the command below. Choose `Bob` as the nickname.
+```
+./build/chat2 --staticnode:/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAkyTos6LeGrj1YJyA3WYzp9qKQGCsxbtvyoBRHSu9PCrQZ --content-topic:/toy-chat/3/mingde/proto --ports-shift=2 --fleet:none --nodekey=9ab635854ffe8fed32b17d7ef38e0b2f354ca1f3283b7f78fb77227004d2cbe6 --rln-relay:true --rln-relay-membership-index:2
+
+Choose a nickname >> Bob
+```
+
+**Carol setup**:
+Run the following command to set up the third chat2 client, and choose `Carol` as the nickname.
+```
+./build/chat2 --staticnode:/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAkyTos6LeGrj1YJyA3WYzp9qKQGCsxbtvyoBRHSu9PCrQZ --content-topic:/toy-chat/3/mingde/proto --ports-shift=3 --fleet:none --nodekey=0aa89d7f27300c9fb4e119acc225c8873a3bf96bbb4c82045c94934bcc6a6af8 --rln-relay:true --rln-relay-membership-index:3
+
+Choose a nickname >> Carol
+
+```
+
+# Run the test
+Now that the network is formed, you can start chatting.
+For a better illustration of spam protection, use `Bob` and `Carol` clients for chatting and let `Alice` act only as a router.
+Once you type a chat line and hit enter, you will see a message that indicates the epoch at which the message is sent e.g.,
+```
+>> Hi!
+--rln epoch: 164495684
+ Bob: Hi!
+```
+The numerical value `164495684` indicates the epoch of the message `Hi!`.
+You will see a different value than `164495684` on your screen.
+If two messages sent by the same chat2 client happen to have the same RLN epoch value, then one of them will be detected as spam and won't be routed (by Alice in this test setting).
+At the time of this tutorial, the epoch duration is set to `10` seconds.
+Thus, if you send two messages less than `10` seconds apart, they are likely to get the same `rln epoch` values.
+
+After sending a chat message, you may experience some delay before the next chat prompt appears.
+The reason is that under the hood a zero-knowledge proof is being generated and attached to your message.
+
+Once you are done with the test, make sure you close all the chat2 clients by typing `/exit` command.
+```
+>> /exit
+quitting...
+```
+
+# Sample test output
+
+In the following sample test, `Bob` sends three messages namely, `message1`, `message2`, and `message3`.
+The two messages `message2` and `message3` have identical RLN epoch value of `164504930`, so, one of them will be discarded by `Alice` as a spam message.
+You can check this fact by looking at the `Alice` console, where `A spam message is found and discarded : Bob: message3` is presented.
+`Alice` does not relay `message3` further, hence `Carol` never receives it.
+
+Bob
+```
+./build/chat2 --staticnode:/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAkyTos6LeGrj1YJyA3WYzp9qKQGCsxbtvyoBRHSu9PCrQZ --content-topic:/toy-chat/3/mingde/proto --ports-shift=2 --fleet:none --nodekey=9ab635854ffe8fed32b17d7ef38e0b2f354ca1f3283b7f78fb77227004d2cbe6 --rln-relay:true --rln-relay-membership-index:2
+Choose a nickname >> Bob
+Welcome, Bob!
+Connecting to nodes
+Listening on
+ /ip4/75.157.120.249/tcp/60002/p2p/16Uiu2HAmKdCdP89q6CwLc6PeFDJnVR1EmM7fTgtphHiacSNBnuAz
+>> message1
+--rln epoch: 164504929
+ Bob: message1
+>> message2
+--rln epoch: 164504930
+ Bob: message2
+>> message3
+--rln epoch: 164504930
+ Bob: message3
+>> message4
+--rln epoch: 164504973
+ Bob: message4
+>> /exit
+quitting...
+```
+
+Alice
+```
+./build/chat2 --staticnode:/ip4/127.0.0.1/tcp/60010/p2p/16Uiu2HAmKdCdP89q6CwLc6PeFDJnVR1EmM7fTgtphHiacSNBnuAz --content-topic:/toy-chat/3/mingde/proto --ports-shift=1 --fleet:none --nodekey=f157b19b13e9ee818acfc9d3d7eec6b81f70c0a978dec19def261172acbe26e6 --rln-relay:true --rln-relay-membership-index:1
+
+Choose a nickname >> Alice
+Welcome, Alice!
+Connecting to nodes
+Listening on
+ /ip4/75.157.120.249/tcp/60001/p2p/16Uiu2HAkyTos6LeGrj1YJyA3WYzp9qKQGCsxbtvyoBRHSu9PCrQZ
+>> Bob: message1
+>> Bob: message2
+>> A spam message is found and discarded : Bob: message3
+ Bob: message4
+>> /exit
+quitting...
+```
+
+Carol
+```
+./build/chat2 --staticnode:/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAkyTos6LeGrj1YJyA3WYzp9qKQGCsxbtvyoBRHSu9PCrQZ --content-topic:/toy-chat/3/mingde/proto --ports-shift=3 --fleet:none --nodekey=0aa89d7f27300c9fb4e119acc225c8873a3bf96bbb4c82045c94934bcc6a6af8 --rln-relay:true --rln-relay-membership-index:3
+Choose a nickname >> Carol
+Welcome, Carol!
+Connecting to nodes
+Listening on
+ /ip4/75.157.120.249/tcp/60003/p2p/16Uiu2HAm1bEDWZqjxfYRvGo1UpjaejkenJVmMFMPMDmgWWGkREJu
+>> Bob: message1
+>> Bob: message2
+>> Bob: message4
+>> /exit
+quitting...
+```
\ No newline at end of file
diff --git a/third-party/nwaku/docs/tutorial/rln-keystore-generator.md b/third-party/nwaku/docs/tutorial/rln-keystore-generator.md
new file mode 100644
index 0000000..4ce7008
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/rln-keystore-generator.md
@@ -0,0 +1,73 @@
+# rln-keystore-generator
+
+This document describes how to run and use the `rln-keystore-generator` tool.
+It is meant to be used to generate and persist a set of valid RLN credentials to be used with rln-relay.
+
+## Pre-requisites
+
+1. An EOA with some ETH to pay for the registration transaction ($PRIVATE_KEY)
+2. An RPC endpoint to connect to an Ethereum node ($RPC_URL)
+
+## Usage
+
+1. First, we compile the binary
+
+ ```bash
+ make -j16 wakunode2
+ ```
+ This command will fetch the rln static library and link it automatically.
+
+
+2. Define the arguments you wish to use
+
+ ```bash
+ export RPC_URL="https://linea-sepolia.infura.io/v3/..."
+ export PRIVATE_KEY="0x..."
+ export RLN_CONTRACT_ADDRESS="0xB9cd878C90E49F797B4431fBF4fb333108CB90e6"
+ export RLN_CREDENTIAL_PATH="rlnKeystore.json"
+ export RLN_CREDENTIAL_PASSWORD="xxx"
+ ```
+
+3. Dry run the command to ensure better degree of execution
+
+ ```bash
+ ./build/wakunode2 generateRlnKeystore \
+ --rln-relay-eth-client-address:$RPC_URL \
+ --rln-relay-eth-private-key:$PRIVATE_KEY \
+ --rln-relay-eth-contract-address:$RLN_CONTRACT_ADDRESS \
+ --rln-relay-cred-path:$RLN_CREDENTIAL_PATH \
+ --rln-relay-cred-password:$RLN_CREDENTIAL_PASSWORD
+ ```
+ By default, the tool will not execute a transaction. It will execute only if `--execute` is passed in.
+
+4. Run the keystore generator with the onchain registration
+
+ ```bash
+ ./build/wakunode2 generateRlnKeystore \
+ --rln-relay-eth-client-address:$RPC_URL \
+ --rln-relay-eth-private-key:$PRIVATE_KEY \
+ --rln-relay-eth-contract-address:$RLN_CONTRACT_ADDRESS \
+ --rln-relay-cred-path:$RLN_CREDENTIAL_PATH \
+ --rln-relay-cred-password:$RLN_CREDENTIAL_PASSWORD \
+ --execute
+ ```
+
+ What this does is -
+ a. generate a set of valid rln credentials
+ b. registers it to the contract address provided
+ c. persists the credentials to the path provided
+
+5. You may now use this keystore with wakunode2 or chat2.
+
+## Troubleshooting
+
+1. `KeystoreCredentialNotFoundError`
+
+ ```
+ KeystoreCredentialNotFoundError: Credential not found in keystore
+ ```
+ This is most likely due to multiple credentials present in the same keystore.
+ To navigate around this, both chat2 and wakunode2 have provided an option to specify the credential index to use (`--rln-relay-membership-index`).
+ Please use this option with the appropriate tree index of the credential you wish to use.
+
+
diff --git a/third-party/nwaku/docs/tutorial/store.md b/third-party/nwaku/docs/tutorial/store.md
new file mode 100644
index 0000000..0d3aee7
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/store.md
@@ -0,0 +1,42 @@
+# Running Store Protocol
+
+> TODO (2023-05-24): Deprecate or fix
+
+## How to
+
+Build:
+
+```
+# make wakunode2 is run as part of scripts2 target
+make scripts2
+```
+
+Run two nodes and connect them:
+
+```
+# Starts listening on 60000 with RPC server on 8545.
+# Note the "listening on address" in logs.
+./build/wakunode2 --ports-shift:0
+
+# Run another node with staticnode argument
+./build/wakunode2 --ports-shift:1 --staticnode:/ip4/0.0.0.0/tcp/60000/p2p/16Uiu2HAmF4tuht6fmna6uDqoSMgFqhUrdaVR6VQRyGr6sCpfS2jp --storenode:/ip4/0.0.0.0/tcp/60000/p2p/16Uiu2HAmF4tuht6fmna6uDqoSMgFqhUrdaVR6VQRyGr6sCpfS2jp
+```
+
+When flag `persist-messages` is passed messages are going to be persisted in-memory.
+If additionally flag `dbpath` is passed with a path, messages are persisted and stored in a database called `store` under the specified path.
+If flag `persist-messages` is not passed, messages are not persisted and stored at all.
+
+
+
+You should see your nodes connecting.
+
+Do basic RPC calls:
+
+```
+./build/rpc_subscribe 8545
+./build/rpc_subscribe 8546
+./build/rpc_publish 8545 # enter your message in STDIN
+./build/rpc_query 8546 # enter your content topic; default is "1"
+```
+
+You should see other node receive something.
diff --git a/third-party/nwaku/docs/tutorial/websocket.md b/third-party/nwaku/docs/tutorial/websocket.md
new file mode 100644
index 0000000..bc07744
--- /dev/null
+++ b/third-party/nwaku/docs/tutorial/websocket.md
@@ -0,0 +1,69 @@
+# Listening on Websocket to Enable Connections With Waku v2 Browser Peers
+
+> TODO (2023-05-24): Deprecate or fix
+
+Currently, nim-waku only supports TCP transport.
+This means it is not possible to directly connect from a browser using [js-waku](https://github.com/waku-org/js-waku/)
+to a nim-waku based node such as wakunode2.
+
+To remediate to this, utilities such as [websockify](https://github.com/novnc/websockify) can be used.
+This tutorial explains how one can setup websockify alongside wakunode2 to accept connections from peer browsers.
+
+Note that popular browsers only accept secure websocket connections (`wss`) in a secure page (`https`),
+hence we will also cover the creation of SSL certificates.
+
+## Creating certificate using cerbot
+
+Feel free to skip this step if you already own SSL certificate for your domain.
+
+To do so, simply follow the instructions at https://certbot.eff.org/.
+
+Note that you do not need to have a web server (e.g. apache, nginx) running to setup wakunode2 with websockify.
+
+## Setting up websockify
+
+You can install [Websockify](https://github.com/novnc/websockify) via your preferred package manager
+or [using Python](https://github.com/novnc/websockify#installing-websockify).
+
+To start websockify, use the following command:
+
+```shell
+sudo websockify \
+--cert /etc/letsencrypt/live//fullchain.pem \
+--key /etc/letsencrypt/live//privkey.pem 0.0.0.0:443 \
+127.0.0.1:
+```
+
+With:
+- `your.domain` being your domain name (.e.g `www.example.org`).
+- `tcp_port` being the port on which wakunode2 is listening, by default `60000`.
+
+Notes:
+- This assumes you used `certbot` to generate certificate, `/etc/letsencrypt/live` is where `certbot store certificates,
+ if you have your own certificates, changes the path and be sure to pass the full certificate chain including
+ the CA certificate to the `--cert` argument.
+- `sudo` is needed because websockify listens on port `443`;
+ You can avoid using `sudo` by using a custom port, just be sure it is open to the internet by checking your firewall.
+
+## Getting your wakunode2's multiaddr
+
+Start `wakunode2` as you usually do,
+be sure to take in account the listening port to reflect it in the websockify command line.
+
+`wakunode2` prints the multiaddr it is listening too at the start of the logs:
+
+```
+INF 2021-06-23 10:37:25.274+10:00 Listening on topics="wakunode" tid=2271871 file=wakunode2.nim:170 full=/ip4/1.2.3.4/tcp/60000/p2p/16Uiu2HAmPRmVHjZSP3U1T9ez4EQBBUsji5RyvAyDGVNgTQajtEQJ
+```
+
+To get the websocket multiaddr, simply change the port and insert `wss` after said port:
+
+```
+/ip4/1.2.3.4/tcp/443/wss/p2p/16Uiu2HAmPRmVHjZSP3U1T9ez4EQBBUsji5RyvAyDGVNgTQajtEQJ
+```
+
+You can also use your domain name instead of ip address:
+
+```
+/dns4/your.domain/tcp/443/wss/p2p/16Uiu2HAmPRmVHjZSP3U1T9ez4EQBBUsji5RyvAyDGVNgTQajtEQJ
+```
diff --git a/third-party/nwaku/env.sh b/third-party/nwaku/env.sh
new file mode 100755
index 0000000..f90ba9a
--- /dev/null
+++ b/third-party/nwaku/env.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# We use ${BASH_SOURCE[0]} instead of $0 to allow sourcing this file
+# and we fall back to a Zsh-specific special var to also support Zsh.
+REL_PATH="$(dirname ${BASH_SOURCE[0]:-${(%):-%x}})"
+ABS_PATH="$(cd ${REL_PATH}; pwd)"
+source ${ABS_PATH}/vendor/nimbus-build-system/scripts/env.sh
+
diff --git a/third-party/nwaku/examples/README.md b/third-party/nwaku/examples/README.md
new file mode 100644
index 0000000..7055cf6
--- /dev/null
+++ b/third-party/nwaku/examples/README.md
@@ -0,0 +1,75 @@
+# Examples
+
+## Compile
+
+Make all examples.
+```console
+make example2
+```
+
+## Waku API
+
+Uses the simplified Waku API to create and start a node,
+you need an RPC endpoint for Linea Sepolia for RLN:
+
+```console
+./build/waku_api --ethRpcEndpoint=https://linea-sepolia.infura.io/v3/
+```
+
+If you can't be bothered but still want to see some action,
+just run the binary and it will use a non-RLN network:
+
+```console
+./build/waku_api
+```
+
+## publisher/subscriber
+
+Within `examples/` you can find a `publisher` and a `subscriber`. The first one publishes messages to the default pubsub topic on a given content topic, and the second one runs forever listening to that pubsub topic and printing the content it receives.
+
+**Some notes:**
+* These examples are meant to work even if you are behind a firewall and you can't be discovered by discv5.
+* You only need to provide a reachable bootstrap peer (see our [fleets](https://fleets.status.im/))
+* The examples are meant to work out of the box.
+* Note that both services wait for some time until a given minimum amount of connections are reached. This is to ensure messages are gossiped.
+
+**Run:**
+
+Wait until the subscriber is ready.
+```console
+./build/subscriber
+```
+
+And run a publisher
+```console
+./build/publisher
+```
+
+See how the subscriber received the messages published by the publisher. Feel free to experiment from different machines in different locations.
+
+## resource-restricted publisher/subscriber (lightpush/filter)
+
+To illustrate publishing and receiving messages on a resource-restricted client,
+`examples/v2` also provides a `lightpush_publisher` and a `filter_subscriber`.
+The `lightpush_publisher` continually publishes messages via a lightpush service node
+to the default pubsub topic on a given content topic.
+The `filter_subscriber` subscribes via a filter service node
+to the same pubsub and content topic.
+It runs forever, maintaining this subscription
+and printing the content it receives.
+
+**Run**
+Start the filter subscriber.
+```console
+./build/filter_subscriber
+```
+
+And run a lightpush publisher
+```console
+./build/lightpush_publisher
+```
+
+See how the filter subscriber receives messages published by the lightpush publisher.
+Neither the publisher nor the subscriber participates in `relay`,
+but instead make use of service nodes to save resources.
+Feel free to experiment from different machines in different locations.
diff --git a/third-party/nwaku/examples/cbindings/README.md b/third-party/nwaku/examples/cbindings/README.md
new file mode 100644
index 0000000..5465cf5
--- /dev/null
+++ b/third-party/nwaku/examples/cbindings/README.md
@@ -0,0 +1,18 @@
+## App description
+This is a very simple example that shows how to invoke libwaku functions from a C program.
+
+## Build
+1. Open terminal
+2. cd to nwaku root folder
+3. make cwaku_example -j8
+
+This will create libwaku.so and cwaku_example binary within the build folder.
+
+## Run
+1. Open terminal
+2. cd to nwaku root folder
+3. export LD_LIBRARY_PATH=build
+4. `./build/cwaku_example --host=0.0.0.0 --port=60001`
+
+Use `./build/cwaku_example --help` to see some other options.
+
diff --git a/third-party/nwaku/examples/cbindings/base64.c b/third-party/nwaku/examples/cbindings/base64.c
new file mode 100644
index 0000000..0f9acdf
--- /dev/null
+++ b/third-party/nwaku/examples/cbindings/base64.c
@@ -0,0 +1,58 @@
+
+#include "base64.h"
+
+// Base64 encoding
+// source: https://nachtimwald.com/2017/11/18/base64-encode-and-decode-in-c/
+size_t b64_encoded_size(size_t inlen)
+{
+ size_t ret;
+
+ ret = inlen;
+ if (inlen % 3 != 0)
+ ret += 3 - (inlen % 3);
+ ret /= 3;
+ ret *= 4;
+
+ return ret;
+}
+
+const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+char *b64_encode(const unsigned char *in, size_t len)
+{
+ char *out;
+ size_t elen;
+ size_t i;
+ size_t j;
+ size_t v;
+
+ if (in == NULL || len == 0)
+ return NULL;
+
+ elen = b64_encoded_size(len);
+ out = malloc(elen+1);
+ out[elen] = '\0';
+
+ for (i=0, j=0; i> 18) & 0x3F];
+ out[j+1] = b64chars[(v >> 12) & 0x3F];
+ if (i+1 < len) {
+ out[j+2] = b64chars[(v >> 6) & 0x3F];
+ } else {
+ out[j+2] = '=';
+ }
+ if (i+2 < len) {
+ out[j+3] = b64chars[v & 0x3F];
+ } else {
+ out[j+3] = '=';
+ }
+ }
+
+ return out;
+}
+
+// End of Base64 encoding
diff --git a/third-party/nwaku/examples/cbindings/base64.h b/third-party/nwaku/examples/cbindings/base64.h
new file mode 100644
index 0000000..37ca50f
--- /dev/null
+++ b/third-party/nwaku/examples/cbindings/base64.h
@@ -0,0 +1,11 @@
+
+#ifndef _BASE64_H_
+#define _BASE64_H_
+
+#include
+
+size_t b64_encoded_size(size_t inlen);
+
+char *b64_encode(const unsigned char *in, size_t len);
+
+#endif
diff --git a/third-party/nwaku/examples/cbindings/waku_example.c b/third-party/nwaku/examples/cbindings/waku_example.c
new file mode 100644
index 0000000..35ac8a2
--- /dev/null
+++ b/third-party/nwaku/examples/cbindings/waku_example.c
@@ -0,0 +1,366 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "base64.h"
+#include "../../library/libwaku.h"
+
+// Shared synchronization variables
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+int callback_executed = 0;
+
+void waitForCallback() {
+ pthread_mutex_lock(&mutex);
+ while (!callback_executed) {
+ pthread_cond_wait(&cond, &mutex);
+ }
+ callback_executed = 0;
+ pthread_mutex_unlock(&mutex);
+}
+
+#define WAKU_CALL(call) \
+do { \
+ int ret = call; \
+ if (ret != 0) { \
+ printf("Failed the call to: %s. Returned code: %d\n", #call, ret); \
+ exit(1); \
+ } \
+ waitForCallback(); \
+} while (0)
+
+struct ConfigNode {
+ char host[128];
+ int port;
+ char key[128];
+ int relay;
+ char peers[2048];
+ int store;
+ char storeNode[2048];
+ char storeRetentionPolicy[64];
+ char storeDbUrl[256];
+ int storeVacuum;
+ int storeDbMigration;
+ int storeMaxNumDbConnections;
+};
+
+// libwaku Context
+void* ctx;
+
+// For the case of C language we don't need to store a particular userData
+void* userData = NULL;
+
+// Arguments parsing
+static char doc[] = "\nC example that shows how to use the waku library.";
+static char args_doc[] = "";
+
+static struct argp_option options[] = {
+ { "host", 'h', "HOST", 0, "IP to listen for for LibP2P traffic. (default: \"0.0.0.0\")"},
+ { "port", 'p', "PORT", 0, "TCP listening port. (default: \"60000\")"},
+ { "key", 'k', "KEY", 0, "P2P node private key as 64 char hex string."},
+ { "relay", 'r', "RELAY", 0, "Enable relay protocol: 1 or 0. (default: 1)"},
+ { "peers", 'a', "PEERS", 0, "Comma-separated list of peer-multiaddress to connect\
+ to. (default: \"\") e.g. \"/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmVFXtAfSj4EiR7mL2KvL4EE2wztuQgUSBoj2Jx2KeXFLN\""},
+ { 0 }
+};
+
+static error_t parse_opt(int key, char *arg, struct argp_state *state) {
+
+ struct ConfigNode *cfgNode = state->input;
+ switch (key) {
+ case 'h':
+ snprintf(cfgNode->host, 128, "%s", arg);
+ break;
+ case 'p':
+ cfgNode->port = atoi(arg);
+ break;
+ case 'k':
+ snprintf(cfgNode->key, 128, "%s", arg);
+ break;
+ case 'r':
+ cfgNode->relay = atoi(arg);
+ break;
+ case 'a':
+ snprintf(cfgNode->peers, 2048, "%s", arg);
+ break;
+ case ARGP_KEY_ARG:
+ if (state->arg_num >= 1) /* Too many arguments. */
+ argp_usage(state);
+ break;
+ case ARGP_KEY_END:
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+void signal_cond() {
+ pthread_mutex_lock(&mutex);
+ callback_executed = 1;
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&mutex);
+}
+
+static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 };
+
+void event_handler(int callerRet, const char* msg, size_t len, void* userData) {
+ if (callerRet == RET_ERR) {
+ printf("Error: %s\n", msg);
+ exit(1);
+ }
+ else if (callerRet == RET_OK) {
+ printf("Receiving event: %s\n", msg);
+ }
+
+ signal_cond();
+}
+
+void on_event_received(int callerRet, const char* msg, size_t len, void* userData) {
+ if (callerRet == RET_ERR) {
+ printf("Error: %s\n", msg);
+ exit(1);
+ }
+ else if (callerRet == RET_OK) {
+ printf("Receiving event: %s\n", msg);
+ }
+}
+
+char* contentTopic = NULL;
+void handle_content_topic(int callerRet, const char* msg, size_t len, void* userData) {
+ if (contentTopic != NULL) {
+ free(contentTopic);
+ }
+
+ contentTopic = malloc(len * sizeof(char) + 1);
+ strcpy(contentTopic, msg);
+ signal_cond();
+}
+
+char* publishResponse = NULL;
+void handle_publish_ok(int callerRet, const char* msg, size_t len, void* userData) {
+ printf("Publish Ok: %s %lu\n", msg, len);
+
+ if (publishResponse != NULL) {
+ free(publishResponse);
+ }
+
+ publishResponse = malloc(len * sizeof(char) + 1);
+ strcpy(publishResponse, msg);
+}
+
+#define MAX_MSG_SIZE 65535
+
+void publish_message(const char* msg) {
+ char jsonWakuMsg[MAX_MSG_SIZE];
+ char *msgPayload = b64_encode(msg, strlen(msg));
+
+ WAKU_CALL( waku_content_topic(ctx,
+ "appName",
+ 1,
+ "contentTopicName",
+ "encoding",
+ handle_content_topic,
+ userData) );
+ snprintf(jsonWakuMsg,
+ MAX_MSG_SIZE,
+ "{\"payload\":\"%s\",\"contentTopic\":\"%s\"}",
+ msgPayload, contentTopic);
+
+ free(msgPayload);
+
+ WAKU_CALL( waku_relay_publish(ctx,
+ "/waku/2/rs/16/32",
+ jsonWakuMsg,
+ 10000 /*timeout ms*/,
+ event_handler,
+ userData) );
+}
+
+void show_help_and_exit() {
+ printf("Wrong parameters\n");
+ exit(1);
+}
+
+void print_default_pubsub_topic(int callerRet, const char* msg, size_t len, void* userData) {
+ printf("Default pubsub topic: %s\n", msg);
+ signal_cond();
+}
+
+void print_waku_version(int callerRet, const char* msg, size_t len, void* userData) {
+ printf("Git Version: %s\n", msg);
+ signal_cond();
+}
+
+// Beginning of UI program logic
+
+enum PROGRAM_STATE {
+ MAIN_MENU,
+ SUBSCRIBE_TOPIC_MENU,
+ CONNECT_TO_OTHER_NODE_MENU,
+ PUBLISH_MESSAGE_MENU
+};
+
+enum PROGRAM_STATE current_state = MAIN_MENU;
+
+void show_main_menu() {
+ printf("\nPlease, select an option:\n");
+ printf("\t1.) Subscribe to topic\n");
+ printf("\t2.) Connect to other node\n");
+ printf("\t3.) Publish a message\n");
+}
+
+void handle_user_input() {
+ char cmd[1024];
+ memset(cmd, 0, 1024);
+ int numRead = read(0, cmd, 1024);
+ if (numRead <= 0) {
+ return;
+ }
+
+ switch (atoi(cmd))
+ {
+ case SUBSCRIBE_TOPIC_MENU:
+ {
+ printf("Indicate the Pubsubtopic to subscribe:\n");
+ char pubsubTopic[128];
+ scanf("%127s", pubsubTopic);
+
+ WAKU_CALL( waku_relay_subscribe(ctx,
+ pubsubTopic,
+ event_handler,
+ userData) );
+ printf("The subscription went well\n");
+
+ show_main_menu();
+ }
+ break;
+
+ case CONNECT_TO_OTHER_NODE_MENU:
+ printf("Connecting to a node. Please indicate the peer Multiaddress:\n");
+ printf("e.g.: /ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmVFXtAfSj4EiR7mL2KvL4EE2wztuQgUSBoj2Jx2KeXFLN\n");
+ char peerAddr[512];
+ scanf("%511s", peerAddr);
+ WAKU_CALL(waku_connect(ctx, peerAddr, 10000 /* timeoutMs */, event_handler, userData));
+ show_main_menu();
+ break;
+
+ case PUBLISH_MESSAGE_MENU:
+ {
+ printf("Type the message to publish:\n");
+ char msg[1024];
+ scanf("%1023s", msg);
+
+ publish_message(msg);
+
+ show_main_menu();
+ }
+ break;
+
+ case MAIN_MENU:
+ break;
+ }
+}
+
+// End of UI program logic
+
+int main(int argc, char** argv) {
+ struct ConfigNode cfgNode;
+ // default values
+ snprintf(cfgNode.host, 128, "0.0.0.0");
+ cfgNode.port = 60000;
+ cfgNode.relay = 1;
+
+ cfgNode.store = 0;
+ snprintf(cfgNode.storeNode, 2048, "");
+ snprintf(cfgNode.storeRetentionPolicy, 64, "time:6000000");
+ snprintf(cfgNode.storeDbUrl, 256, "postgres://postgres:test123@localhost:5432/postgres");
+ cfgNode.storeVacuum = 0;
+ cfgNode.storeDbMigration = 0;
+ cfgNode.storeMaxNumDbConnections = 30;
+
+ if (argp_parse(&argp, argc, argv, 0, 0, &cfgNode)
+ == ARGP_ERR_UNKNOWN) {
+ show_help_and_exit();
+ }
+
+ char jsonConfig[5000];
+ snprintf(jsonConfig, 5000, "{ \
+ \"clusterId\": 16, \
+ \"shards\": [ 1, 32, 64, 128, 256 ], \
+ \"numShardsInNetwork\": 257, \
+ \"listenAddress\": \"%s\", \
+ \"tcpPort\": %d, \
+ \"relay\": %s, \
+ \"store\": %s, \
+ \"storeMessageDbUrl\": \"%s\", \
+ \"storeMessageRetentionPolicy\": \"%s\", \
+ \"storeMaxNumDbConnections\": %d , \
+ \"logLevel\": \"DEBUG\", \
+ \"discv5Discovery\": true, \
+ \"discv5BootstrapNodes\": \
+ [\"enr:-QEKuED9AJm2HGgrRpVaJY2nj68ao_QiPeUT43sK-aRM7sMJ6R4G11OSDOwnvVacgN1sTw-K7soC5dzHDFZgZkHU0u-XAYJpZIJ2NIJpcISnYxMvim11bHRpYWRkcnO4WgAqNiVib290LTAxLmRvLWFtczMuc3RhdHVzLnByb2Quc3RhdHVzLmltBnZfACw2JWJvb3QtMDEuZG8tYW1zMy5zdGF0dXMucHJvZC5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEC3rRtFQSgc24uWewzXaxTY8hDAHB8sgnxr9k8Rjb5GeSDdGNwgnZfg3VkcIIjKIV3YWt1Mg0\", \"enr:-QEcuED7ww5vo2rKc1pyBp7fubBUH-8STHEZHo7InjVjLblEVyDGkjdTI9VdqmYQOn95vuQH-Htku17WSTzEufx-Wg4mAYJpZIJ2NIJpcIQihw1Xim11bHRpYWRkcnO4bAAzNi5ib290LTAxLmdjLXVzLWNlbnRyYWwxLWEuc3RhdHVzLnByb2Quc3RhdHVzLmltBnZfADU2LmJvb3QtMDEuZ2MtdXMtY2VudHJhbDEtYS5zdGF0dXMucHJvZC5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaECxjqgDQ0WyRSOilYU32DA5k_XNlDis3m1VdXkK9xM6kODdGNwgnZfg3VkcIIjKIV3YWt1Mg0\", \"enr:-QEcuEAoShWGyN66wwusE3Ri8hXBaIkoHZHybUB8cCPv5v3ypEf9OCg4cfslJxZFANl90s-jmMOugLUyBx4EfOBNJ6_VAYJpZIJ2NIJpcIQI2hdMim11bHRpYWRkcnO4bAAzNi5ib290LTAxLmFjLWNuLWhvbmdrb25nLWMuc3RhdHVzLnByb2Quc3RhdHVzLmltBnZfADU2LmJvb3QtMDEuYWMtY24taG9uZ2tvbmctYy5zdGF0dXMucHJvZC5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDP7CbRk-YKJwOFFM4Z9ney0GPc7WPJaCwGkpNRyla7mCDdGNwgnZfg3VkcIIjKIV3YWt1Mg0\"], \
+ \"discv5UdpPort\": 9999, \
+ \"dnsDiscoveryUrl\": \"enrtree://AMOJVZX4V6EXP7NTJPMAYJYST2QP6AJXYW76IU6VGJS7UVSNDYZG4@boot.prod.status.nodes.status.im\", \
+ \"dnsDiscoveryNameServers\": [\"8.8.8.8\", \"1.0.0.1\"] \
+ }", cfgNode.host,
+ cfgNode.port,
+ cfgNode.relay ? "true":"false",
+ cfgNode.store ? "true":"false",
+ cfgNode.storeDbUrl,
+ cfgNode.storeRetentionPolicy,
+ cfgNode.storeMaxNumDbConnections);
+
+ ctx = waku_new(jsonConfig, event_handler, userData);
+ waitForCallback();
+
+ WAKU_CALL( waku_default_pubsub_topic(ctx, print_default_pubsub_topic, userData) );
+ WAKU_CALL( waku_version(ctx, print_waku_version, userData) );
+
+ printf("Bind addr: %s:%u\n", cfgNode.host, cfgNode.port);
+ printf("Waku Relay enabled: %s\n", cfgNode.relay == 1 ? "YES": "NO");
+
+ waku_set_event_callback(ctx, on_event_received, userData);
+
+ waku_start(ctx, event_handler, userData);
+ waitForCallback();
+
+ WAKU_CALL( waku_listen_addresses(ctx, event_handler, userData) );
+
+ WAKU_CALL( waku_relay_subscribe(ctx,
+ "/waku/2/rs/0/0",
+ event_handler,
+ userData) );
+
+ WAKU_CALL( waku_discv5_update_bootnodes(ctx,
+ "[\"enr:-QEkuEBIkb8q8_mrorHndoXH9t5N6ZfD-jehQCrYeoJDPHqT0l0wyaONa2-piRQsi3oVKAzDShDVeoQhy0uwN1xbZfPZAYJpZIJ2NIJpcIQiQlleim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwA2Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQKnGt-GSgqPSf3IAPM7bFgTlpczpMZZLF3geeoNNsxzSoN0Y3CCdl-DdWRwgiMohXdha3UyDw\",\"enr:-QEkuEB3WHNS-xA3RDpfu9A2Qycr3bN3u7VoArMEiDIFZJ66F1EB3d4wxZN1hcdcOX-RfuXB-MQauhJGQbpz3qUofOtLAYJpZIJ2NIJpcIQI2SVcim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmFjLWNuLWhvbmdrb25nLWMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwA2Ni9ub2RlLTAxLmFjLWNuLWhvbmdrb25nLWMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQPK35Nnz0cWUtSAhBp7zvHEhyU_AqeQUlqzLiLxfP2L4oN0Y3CCdl-DdWRwgiMohXdha3UyDw\"]",
+ event_handler,
+ userData) );
+
+ WAKU_CALL( waku_get_peerids_from_peerstore(ctx,
+ event_handler,
+ userData) );
+
+ show_main_menu();
+ while(1) {
+ handle_user_input();
+
+ // Uncomment the following if need to test the metrics retrieval
+ // WAKU_CALL( waku_get_metrics(ctx,
+ // event_handler,
+ // userData) );
+ }
+
+ pthread_mutex_destroy(&mutex);
+ pthread_cond_destroy(&cond);
+}
diff --git a/third-party/nwaku/examples/cpp/README.md b/third-party/nwaku/examples/cpp/README.md
new file mode 100644
index 0000000..fa8d246
--- /dev/null
+++ b/third-party/nwaku/examples/cpp/README.md
@@ -0,0 +1,18 @@
+## App description
+This is a very simple example that shows how to invoke libwaku functions from a C++ program.
+
+## Build
+1. Open terminal
+2. cd to nwaku root folder
+3. make cppwaku_example -j8
+
+This will create libwaku.so and cppwaku_example binary within the build folder.
+
+## Run
+1. Open terminal
+2. cd to nwaku root folder
+3. export LD_LIBRARY_PATH=build
+4. `./build/cppwaku_example --host=0.0.0.0 --port=60001`
+
+Use `./build/cppwaku_example --help` to see some other options.
+
diff --git a/third-party/nwaku/examples/cpp/base64.cpp b/third-party/nwaku/examples/cpp/base64.cpp
new file mode 100644
index 0000000..517e4b2
--- /dev/null
+++ b/third-party/nwaku/examples/cpp/base64.cpp
@@ -0,0 +1,53 @@
+
+#include
+#include "base64.h"
+
+// Base64 encoding
+// source: https://nachtimwald.com/2017/11/18/base64-encode-and-decode-in-c/
+size_t b64_encoded_size(size_t inlen)
+{
+ size_t ret;
+
+ ret = inlen;
+ if (inlen % 3 != 0)
+ ret += 3 - (inlen % 3);
+ ret /= 3;
+ ret *= 4;
+
+ return ret;
+}
+
+const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+void b64_encode(char* in, size_t len, std::vector& out)
+{
+ size_t elen;
+ size_t i;
+ size_t j;
+ size_t v;
+
+ if (in == NULL || len == 0)
+ return;
+
+ elen = b64_encoded_size(len);
+ out.reserve(elen+1);
+
+ for (i=0, j=0; i> 18) & 0x3F];
+ out[j+1] = b64chars[(v >> 12) & 0x3F];
+ if (i+1 < len) {
+ out[j+2] = b64chars[(v >> 6) & 0x3F];
+ } else {
+ out[j+2] = '=';
+ }
+ if (i+2 < len) {
+ out[j+3] = b64chars[v & 0x3F];
+ } else {
+ out[j+3] = '=';
+ }
+ }
+}
diff --git a/third-party/nwaku/examples/cpp/base64.h b/third-party/nwaku/examples/cpp/base64.h
new file mode 100644
index 0000000..0994a0b
--- /dev/null
+++ b/third-party/nwaku/examples/cpp/base64.h
@@ -0,0 +1,11 @@
+
+#ifndef _BASE64_H_
+#define _BASE64_H_
+
+#include
+
+size_t b64_encoded_size(size_t inlen);
+
+void b64_encode(char* in, size_t len, std::vector& out);
+
+#endif
diff --git a/third-party/nwaku/examples/cpp/waku.cpp b/third-party/nwaku/examples/cpp/waku.cpp
new file mode 100644
index 0000000..c47877d
--- /dev/null
+++ b/third-party/nwaku/examples/cpp/waku.cpp
@@ -0,0 +1,331 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "base64.h"
+#include "../../library/libwaku.h"
+
+// Shared synchronization variables
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+int callback_executed = 0;
+
+void waitForCallback() {
+ pthread_mutex_lock(&mutex);
+ while (!callback_executed) {
+ pthread_cond_wait(&cond, &mutex);
+ }
+ callback_executed = 0;
+ pthread_mutex_unlock(&mutex);
+}
+
+void signal_cond() {
+ pthread_mutex_lock(&mutex);
+ callback_executed = 1;
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&mutex);
+}
+
+#define WAKU_CALL(call) \
+do { \
+ int ret = call; \
+ if (ret != 0) { \
+ std::cout << "Failed the call to: " << #call << ". Code: " << ret << "\n"; \
+ } \
+ waitForCallback(); \
+} while (0)
+
+struct ConfigNode {
+ char host[128];
+ int port;
+ char key[128];
+ int relay;
+ char peers[2048];
+};
+
+// Arguments parsing
+static char doc[] = "\nC example that shows how to use the waku library.";
+static char args_doc[] = "";
+
+static struct argp_option options[] = {
+ { "host", 'h', "HOST", 0, "IP to listen for for LibP2P traffic. (default: \"0.0.0.0\")"},
+ { "port", 'p', "PORT", 0, "TCP listening port. (default: \"60000\")"},
+ { "key", 'k', "KEY", 0, "P2P node private key as 64 char hex string."},
+ { "relay", 'r', "RELAY", 0, "Enable relay protocol: 1 or 0. (default: 1)"},
+ { "peers", 'a', "PEERS", 0, "Comma-separated list of peer-multiaddress to connect\
+ to. (default: \"\") e.g. \"/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmVFXtAfSj4EiR7mL2KvL4EE2wztuQgUSBoj2Jx2KeXFLN\""},
+ { 0 }
+};
+
+static error_t parse_opt(int key, char *arg, struct argp_state *state) {
+
+ struct ConfigNode *cfgNode = (ConfigNode *) state->input;
+ switch (key) {
+ case 'h':
+ snprintf(cfgNode->host, 128, "%s", arg);
+ break;
+ case 'p':
+ cfgNode->port = atoi(arg);
+ break;
+ case 'k':
+ snprintf(cfgNode->key, 128, "%s", arg);
+ break;
+ case 'r':
+ cfgNode->relay = atoi(arg);
+ break;
+ case 'a':
+ snprintf(cfgNode->peers, 2048, "%s", arg);
+ break;
+ case ARGP_KEY_ARG:
+ if (state->arg_num >= 1) /* Too many arguments. */
+ argp_usage(state);
+ break;
+ case ARGP_KEY_END:
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+void event_handler(const char* msg, size_t len) {
+ printf("Receiving event: %s\n", msg);
+}
+
+void handle_error(const char* msg, size_t len) {
+ printf("handle_error: %s\n", msg);
+ exit(1);
+}
+
+template
+auto cify(F&& f) {
+ static F fn = std::forward(f);
+ return [](int callerRet, const char* msg, size_t len, void* userData) {
+ signal_cond();
+ return fn(msg, len);
+ };
+}
+
+static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 };
+
+// Beginning of UI program logic
+
+enum PROGRAM_STATE {
+ MAIN_MENU,
+ SUBSCRIBE_TOPIC_MENU,
+ CONNECT_TO_OTHER_NODE_MENU,
+ PUBLISH_MESSAGE_MENU
+};
+
+enum PROGRAM_STATE current_state = MAIN_MENU;
+
+void show_main_menu() {
+ printf("\nPlease, select an option:\n");
+ printf("\t1.) Subscribe to topic\n");
+ printf("\t2.) Connect to other node\n");
+ printf("\t3.) Publish a message\n");
+}
+
+void handle_user_input(void* ctx) {
+ char cmd[1024];
+ memset(cmd, 0, 1024);
+ int numRead = read(0, cmd, 1024);
+ if (numRead <= 0) {
+ return;
+ }
+
+ switch (atoi(cmd))
+ {
+ case SUBSCRIBE_TOPIC_MENU:
+ {
+ printf("Indicate the Pubsubtopic to subscribe:\n");
+ char pubsubTopic[128];
+ scanf("%127s", pubsubTopic);
+
+ WAKU_CALL( waku_relay_subscribe(ctx,
+ pubsubTopic,
+ cify([&](const char* msg, size_t len) {
+ event_handler(msg, len);
+ }),
+ nullptr) );
+ printf("The subscription went well\n");
+
+ show_main_menu();
+ }
+ break;
+
+ case CONNECT_TO_OTHER_NODE_MENU:
+ printf("Connecting to a node. Please indicate the peer Multiaddress:\n");
+ printf("e.g.: /ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmVFXtAfSj4EiR7mL2KvL4EE2wztuQgUSBoj2Jx2KeXFLN\n");
+ char peerAddr[512];
+ scanf("%511s", peerAddr);
+ WAKU_CALL( waku_connect(ctx,
+ peerAddr,
+ 10000 /* timeoutMs */,
+ cify([&](const char* msg, size_t len) {
+ event_handler(msg, len);
+ }),
+ nullptr));
+ show_main_menu();
+ break;
+
+ case PUBLISH_MESSAGE_MENU:
+ {
+ printf("Type the message to publish:\n");
+ char msg[1024];
+ scanf("%1023s", msg);
+
+ char jsonWakuMsg[2048];
+ std::vector msgPayload;
+ b64_encode(msg, strlen(msg), msgPayload);
+
+ std::string contentTopic;
+ waku_content_topic(ctx,
+ "appName",
+ 1,
+ "contentTopicName",
+ "encoding",
+ cify([&contentTopic](const char* msg, size_t len) {
+ contentTopic = msg;
+ }),
+ nullptr);
+
+ snprintf(jsonWakuMsg,
+ 2048,
+ "{\"payload\":\"%s\",\"contentTopic\":\"%s\"}",
+ msgPayload.data(), contentTopic.c_str());
+
+ WAKU_CALL( waku_relay_publish(ctx,
+ "/waku/2/rs/16/32",
+ jsonWakuMsg,
+ 10000 /*timeout ms*/,
+ cify([&](const char* msg, size_t len) {
+ event_handler(msg, len);
+ }),
+ nullptr) );
+
+ show_main_menu();
+ }
+ break;
+
+ case MAIN_MENU:
+ break;
+ }
+}
+
+// End of UI program logic
+
+void show_help_and_exit() {
+ printf("Wrong parameters\n");
+ exit(1);
+}
+
+int main(int argc, char** argv) {
+ struct ConfigNode cfgNode;
+ // default values
+ snprintf(cfgNode.host, 128, "0.0.0.0");
+ snprintf(cfgNode.key, 128,
+ "364d111d729a6eb6d2e6113e163f017b5ef03a6f94c9b5b7bb1bb36fa5cb07a9");
+ cfgNode.port = 60000;
+ cfgNode.relay = 1;
+
+ if (argp_parse(&argp, argc, argv, 0, 0, &cfgNode)
+ == ARGP_ERR_UNKNOWN) {
+ show_help_and_exit();
+ }
+
+ char jsonConfig[2048];
+ snprintf(jsonConfig, 2048, "{ \
+ \"host\": \"%s\", \
+ \"port\": %d, \
+ \"relay\": true, \
+ \"clusterId\": 16, \
+ \"shards\": [ 1, 32, 64, 128, 256 ], \
+ \"logLevel\": \"FATAL\", \
+ \"discv5Discovery\": true, \
+ \"discv5BootstrapNodes\": \
+ [\"enr:-QESuEB4Dchgjn7gfAvwB00CxTA-nGiyk-aALI-H4dYSZD3rUk7bZHmP8d2U6xDiQ2vZffpo45Jp7zKNdnwDUx6g4o6XAYJpZIJ2NIJpcIRA4VDAim11bHRpYWRkcnO4XAArNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwAtNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQOvD3S3jUNICsrOILlmhENiWAMmMVlAl6-Q8wRB7hidY4N0Y3CCdl-DdWRwgiMohXdha3UyDw\", \"enr:-QEkuEBIkb8q8_mrorHndoXH9t5N6ZfD-jehQCrYeoJDPHqT0l0wyaONa2-piRQsi3oVKAzDShDVeoQhy0uwN1xbZfPZAYJpZIJ2NIJpcIQiQlleim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwA2Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQKnGt-GSgqPSf3IAPM7bFgTlpczpMZZLF3geeoNNsxzSoN0Y3CCdl-DdWRwgiMohXdha3UyDw\"], \
+ \"discv5UdpPort\": 9999, \
+ \"dnsDiscoveryUrl\": \"enrtree://AMOJVZX4V6EXP7NTJPMAYJYST2QP6AJXYW76IU6VGJS7UVSNDYZG4@boot.prod.status.nodes.status.im\", \
+ \"dnsDiscoveryNameServers\": [\"8.8.8.8\", \"1.0.0.1\"] \
+ }", cfgNode.host,
+ cfgNode.port);
+
+ void* ctx =
+ waku_new(jsonConfig,
+ cify([](const char* msg, size_t len) {
+ std::cout << "waku_new feedback: " << msg << std::endl;
+ }
+ ),
+ nullptr
+ );
+ waitForCallback();
+
+ // example on how to retrieve a value from the `libwaku` callback.
+ std::string defaultPubsubTopic;
+ WAKU_CALL(
+ waku_default_pubsub_topic(
+ ctx,
+ cify([&defaultPubsubTopic](const char* msg, size_t len) {
+ defaultPubsubTopic = msg;
+ }
+ ),
+ nullptr));
+
+ std::cout << "Default pubsub topic: " << defaultPubsubTopic << std::endl;
+
+ WAKU_CALL(waku_version(ctx,
+ cify([&](const char* msg, size_t len) {
+ std::cout << "Git Version: " << msg << std::endl;
+ }),
+ nullptr));
+
+ printf("Bind addr: %s:%u\n", cfgNode.host, cfgNode.port);
+ printf("Waku Relay enabled: %s\n", cfgNode.relay == 1 ? "YES": "NO");
+
+ std::string pubsubTopic;
+ WAKU_CALL(waku_pubsub_topic(ctx,
+ "example",
+ cify([&](const char* msg, size_t len) {
+ pubsubTopic = msg;
+ }),
+ nullptr));
+
+ std::cout << "Custom pubsub topic: " << pubsubTopic << std::endl;
+
+ waku_set_event_callback(ctx,
+ cify([&](const char* msg, size_t len) {
+ event_handler(msg, len);
+ }),
+ nullptr);
+
+ WAKU_CALL( waku_start(ctx,
+ cify([&](const char* msg, size_t len) {
+ event_handler(msg, len);
+ }),
+ nullptr));
+
+ WAKU_CALL( waku_relay_subscribe(ctx,
+ defaultPubsubTopic.c_str(),
+ cify([&](const char* msg, size_t len) {
+ event_handler(msg, len);
+ }),
+ nullptr) );
+
+ show_main_menu();
+ while(1) {
+ handle_user_input(ctx);
+ }
+}
diff --git a/third-party/nwaku/examples/filter_subscriber.nim b/third-party/nwaku/examples/filter_subscriber.nim
new file mode 100644
index 0000000..e4e26bd
--- /dev/null
+++ b/third-party/nwaku/examples/filter_subscriber.nim
@@ -0,0 +1,117 @@
+import
+ std/[tables, sequtils],
+ stew/byteutils,
+ chronicles,
+ chronos,
+ confutils,
+ libp2p/crypto/crypto,
+ eth/keys,
+ eth/p2p/discoveryv5/enr
+
+import
+ waku/[
+ common/logging,
+ node/peer_manager,
+ waku_core,
+ waku_node,
+ waku_enr,
+ discovery/waku_discv5,
+ factory/builder,
+ waku_relay,
+ waku_filter_v2/client,
+ ]
+
+# careful if running pub and sub in the same machine
+const wakuPort = 50000
+
+const clusterId = 1
+const shardId = @[0'u16]
+
+const
+ FilterPeer =
+ "/ip4/64.225.80.192/tcp/30303/p2p/16Uiu2HAmNaeL4p3WEYzC9mgXBmBWSgWjPHRvatZTXnp8Jgv3iKsb"
+ FilterPubsubTopic = PubsubTopic("/waku/2/rs/1/0")
+ FilterContentTopic = ContentTopic("/examples/1/light-pubsub-example/proto")
+
+proc messagePushHandler(
+ pubsubTopic: PubsubTopic, message: WakuMessage
+) {.async, gcsafe.} =
+ let payloadStr = string.fromBytes(message.payload)
+ notice "message received",
+ payload = payloadStr,
+ pubsubTopic = pubsubTopic,
+ contentTopic = message.contentTopic,
+ timestamp = message.timestamp
+
+proc setupAndSubscribe(rng: ref HmacDrbgContext) {.async.} =
+ # use notice to filter all waku messaging
+ setupLog(logging.LogLevel.NOTICE, logging.LogFormat.TEXT)
+
+ notice "starting subscriber", wakuPort = wakuPort
+ let
+ nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
+ ip = parseIpAddress("0.0.0.0")
+ flags = CapabilitiesBitfield.init(relay = true)
+
+ let relayShards = RelayShards.init(clusterId, shardId).valueOr:
+ error "Relay shards initialization failed", error = error
+ quit(QuitFailure)
+
+ var enrBuilder = EnrBuilder.init(nodeKey)
+ enrBuilder.withWakuRelaySharding(relayShards).expect(
+ "Building ENR with relay sharding failed"
+ )
+
+ let recordRes = enrBuilder.build()
+ let record =
+ if recordRes.isErr():
+ error "failed to create enr record", error = recordRes.error
+ quit(QuitFailure)
+ else:
+ recordRes.get()
+
+ var builder = WakuNodeBuilder.init()
+ builder.withNodeKey(nodeKey)
+ builder.withRecord(record)
+ builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet()
+ let node = builder.build().tryGet()
+
+ node.mountMetadata(clusterId, shardId).expect(
+ "failed to mount waku metadata protocol"
+ )
+ await node.mountFilterClient()
+
+ await node.start()
+
+ node.peerManager.start()
+
+ node.wakuFilterClient.registerPushHandler(messagePushHandler)
+
+ let filterPeer = parsePeerInfo(FilterPeer).get()
+
+ while true:
+ notice "maintaining subscription"
+ # First use filter-ping to check if we have an active subscription
+ let pingRes = await node.wakuFilterClient.ping(filterPeer)
+ if pingRes.isErr():
+ # No subscription found. Let's subscribe.
+ notice "no subscription found. Sending subscribe request"
+
+ let subscribeRes = await node.wakuFilterClient.subscribe(
+ filterPeer, FilterPubsubTopic, @[FilterContentTopic]
+ )
+
+ if subscribeRes.isErr():
+ notice "subscribe request failed. Quitting.", err = subscribeRes.error
+ break
+ else:
+ notice "subscribe request successful."
+ else:
+ notice "subscription found."
+
+ await sleepAsync(60.seconds) # Subscription maintenance interval
+
+when isMainModule:
+ let rng = crypto.newRng()
+ asyncSpawn setupAndSubscribe(rng)
+ runForever()
diff --git a/third-party/nwaku/examples/golang/README.md b/third-party/nwaku/examples/golang/README.md
new file mode 100644
index 0000000..39d4882
--- /dev/null
+++ b/third-party/nwaku/examples/golang/README.md
@@ -0,0 +1,44 @@
+
+## Pre-requisite
+libwaku.so is needed to be compiled and present in build folder. To create it:
+
+- Run only the first time and after changing the current commit
+```code
+make update
+```
+- Run the next every time you want to compile libwaku
+```code
+make POSTGRES=1 libwaku -j4
+```
+
+Also needed:
+
+- Install libpq (needed by Postgres client)
+ - On Linux:
+```code
+sudo apt install libpq5
+```
+ - On MacOS (not tested)
+```code
+brew install libpq
+```
+
+## Compilation
+
+From the nwaku root folder:
+
+```code
+go build -o waku-go examples/golang/waku.go
+```
+
+## Run
+From the nwaku root folder:
+
+
+```code
+export LD_LIBRARY_PATH=build
+```
+
+```code
+./waku-go
+```
diff --git a/third-party/nwaku/examples/golang/waku.go b/third-party/nwaku/examples/golang/waku.go
new file mode 100644
index 0000000..846362d
--- /dev/null
+++ b/third-party/nwaku/examples/golang/waku.go
@@ -0,0 +1,652 @@
+package main
+
+/*
+ #cgo LDFLAGS: -L../../build/ -lwaku
+ #cgo LDFLAGS: -L../../ -Wl,-rpath,../../
+
+ #include "../../library/libwaku.h"
+ #include
+ #include
+
+ extern void globalEventCallback(int ret, char* msg, size_t len, void* userData);
+
+ typedef struct {
+ int ret;
+ char* msg;
+ size_t len;
+ } Resp;
+
+ static void* allocResp() {
+ return calloc(1, sizeof(Resp));
+ }
+
+ static void freeResp(void* resp) {
+ if (resp != NULL) {
+ free(resp);
+ }
+ }
+
+ static char* getMyCharPtr(void* resp) {
+ if (resp == NULL) {
+ return NULL;
+ }
+ Resp* m = (Resp*) resp;
+ return m->msg;
+ }
+
+ static size_t getMyCharLen(void* resp) {
+ if (resp == NULL) {
+ return 0;
+ }
+ Resp* m = (Resp*) resp;
+ return m->len;
+ }
+
+ static int getRet(void* resp) {
+ if (resp == NULL) {
+ return 0;
+ }
+ Resp* m = (Resp*) resp;
+ return m->ret;
+ }
+
+ // resp must be set != NULL in case interest on retrieving data from the callback
+ static void callback(int ret, char* msg, size_t len, void* resp) {
+ if (resp != NULL) {
+ Resp* m = (Resp*) resp;
+ m->ret = ret;
+ m->msg = msg;
+ m->len = len;
+ }
+ }
+
+ #define WAKU_CALL(call) \
+ do { \
+ int ret = call; \
+ if (ret != 0) { \
+ printf("Failed the call to: %s. Returned code: %d\n", #call, ret); \
+ exit(1); \
+ } \
+ } while (0)
+
+ static void* cGoWakuNew(const char* configJson, void* resp) {
+ // We pass NULL because we are not interested in retrieving data from this callback
+ void* ret = waku_new(configJson, (WakuCallBack) callback, resp);
+ return ret;
+ }
+
+ static void cGoWakuStart(void* wakuCtx, void* resp) {
+ WAKU_CALL(waku_start(wakuCtx, (WakuCallBack) callback, resp));
+ }
+
+ static void cGoWakuStop(void* wakuCtx, void* resp) {
+ WAKU_CALL(waku_stop(wakuCtx, (WakuCallBack) callback, resp));
+ }
+
+ static void cGoWakuDestroy(void* wakuCtx, void* resp) {
+ WAKU_CALL(waku_destroy(wakuCtx, (WakuCallBack) callback, resp));
+ }
+
+ static void cGoWakuStartDiscV5(void* wakuCtx, void* resp) {
+ WAKU_CALL(waku_start_discv5(wakuCtx, (WakuCallBack) callback, resp));
+ }
+
+ static void cGoWakuStopDiscV5(void* wakuCtx, void* resp) {
+ WAKU_CALL(waku_stop_discv5(wakuCtx, (WakuCallBack) callback, resp));
+ }
+
+ static void cGoWakuVersion(void* wakuCtx, void* resp) {
+ WAKU_CALL(waku_version(wakuCtx, (WakuCallBack) callback, resp));
+ }
+
+ static void cGoWakuSetEventCallback(void* wakuCtx) {
+ // The 'globalEventCallback' Go function is shared amongst all possible Waku instances.
+
+ // Given that the 'globalEventCallback' is shared, we pass again the
+ // wakuCtx instance but in this case is needed to pick up the correct method
+ // that will handle the event.
+
+ // In other words, for every call the libwaku makes to globalEventCallback,
+ // the 'userData' parameter will bring the context of the node that registered
+ // that globalEventCallback.
+
+ // This technique is needed because cgo only allows to export Go functions and not methods.
+
+ waku_set_event_callback(wakuCtx, (WakuCallBack) globalEventCallback, wakuCtx);
+ }
+
+ static void cGoWakuContentTopic(void* wakuCtx,
+ char* appName,
+ int appVersion,
+ char* contentTopicName,
+ char* encoding,
+ void* resp) {
+
+ WAKU_CALL( waku_content_topic(wakuCtx,
+ appName,
+ appVersion,
+ contentTopicName,
+ encoding,
+ (WakuCallBack) callback,
+ resp) );
+ }
+
+ static void cGoWakuPubsubTopic(void* wakuCtx, char* topicName, void* resp) {
+ WAKU_CALL( waku_pubsub_topic(wakuCtx, topicName, (WakuCallBack) callback, resp) );
+ }
+
+ static void cGoWakuDefaultPubsubTopic(void* wakuCtx, void* resp) {
+ WAKU_CALL (waku_default_pubsub_topic(wakuCtx, (WakuCallBack) callback, resp));
+ }
+
+ static void cGoWakuRelayPublish(void* wakuCtx,
+ const char* pubSubTopic,
+ const char* jsonWakuMessage,
+ int timeoutMs,
+ void* resp) {
+
+ WAKU_CALL (waku_relay_publish(wakuCtx,
+ pubSubTopic,
+ jsonWakuMessage,
+ timeoutMs,
+ (WakuCallBack) callback,
+ resp));
+ }
+
+ static void cGoWakuRelaySubscribe(void* wakuCtx, char* pubSubTopic, void* resp) {
+ WAKU_CALL ( waku_relay_subscribe(wakuCtx,
+ pubSubTopic,
+ (WakuCallBack) callback,
+ resp) );
+ }
+
+ static void cGoWakuRelayUnsubscribe(void* wakuCtx, char* pubSubTopic, void* resp) {
+
+ WAKU_CALL ( waku_relay_unsubscribe(wakuCtx,
+ pubSubTopic,
+ (WakuCallBack) callback,
+ resp) );
+ }
+
+ static void cGoWakuConnect(void* wakuCtx, char* peerMultiAddr, int timeoutMs, void* resp) {
+ WAKU_CALL( waku_connect(wakuCtx,
+ peerMultiAddr,
+ timeoutMs,
+ (WakuCallBack) callback,
+ resp) );
+ }
+
+ static void cGoWakuDialPeerById(void* wakuCtx,
+ char* peerId,
+ char* protocol,
+ int timeoutMs,
+ void* resp) {
+
+ WAKU_CALL( waku_dial_peer_by_id(wakuCtx,
+ peerId,
+ protocol,
+ timeoutMs,
+ (WakuCallBack) callback,
+ resp) );
+ }
+
+ static void cGoWakuDisconnectPeerById(void* wakuCtx, char* peerId, void* resp) {
+ WAKU_CALL( waku_disconnect_peer_by_id(wakuCtx,
+ peerId,
+ (WakuCallBack) callback,
+ resp) );
+ }
+
+ static void cGoWakuListenAddresses(void* wakuCtx, void* resp) {
+ WAKU_CALL (waku_listen_addresses(wakuCtx, (WakuCallBack) callback, resp) );
+ }
+
+ static void cGoWakuGetMyENR(void* ctx, void* resp) {
+ WAKU_CALL (waku_get_my_enr(ctx, (WakuCallBack) callback, resp) );
+ }
+
+ static void cGoWakuGetMyPeerId(void* ctx, void* resp) {
+ WAKU_CALL (waku_get_my_peerid(ctx, (WakuCallBack) callback, resp) );
+ }
+
+ static void cGoWakuListPeersInMesh(void* ctx, char* pubSubTopic, void* resp) {
+ WAKU_CALL (waku_relay_get_num_peers_in_mesh(ctx, pubSubTopic, (WakuCallBack) callback, resp) );
+ }
+
+ static void cGoWakuGetNumConnectedPeers(void* ctx, char* pubSubTopic, void* resp) {
+ WAKU_CALL (waku_relay_get_num_connected_peers(ctx, pubSubTopic, (WakuCallBack) callback, resp) );
+ }
+
+ static void cGoWakuGetPeerIdsFromPeerStore(void* wakuCtx, void* resp) {
+ WAKU_CALL (waku_get_peerids_from_peerstore(wakuCtx, (WakuCallBack) callback, resp) );
+ }
+
+ static void cGoWakuLightpushPublish(void* wakuCtx,
+ const char* pubSubTopic,
+ const char* jsonWakuMessage,
+ void* resp) {
+
+ WAKU_CALL (waku_lightpush_publish(wakuCtx,
+ pubSubTopic,
+ jsonWakuMessage,
+ (WakuCallBack) callback,
+ resp));
+ }
+
+ static void cGoWakuStoreQuery(void* wakuCtx,
+ const char* jsonQuery,
+ const char* peerAddr,
+ int timeoutMs,
+ void* resp) {
+
+ WAKU_CALL (waku_store_query(wakuCtx,
+ jsonQuery,
+ peerAddr,
+ timeoutMs,
+ (WakuCallBack) callback,
+ resp));
+ }
+
+ static void cGoWakuPeerExchangeQuery(void* wakuCtx,
+ uint64_t numPeers,
+ void* resp) {
+
+ WAKU_CALL (waku_peer_exchange_request(wakuCtx,
+ numPeers,
+ (WakuCallBack) callback,
+ resp));
+ }
+
+ static void cGoWakuGetPeerIdsByProtocol(void* wakuCtx,
+ const char* protocol,
+ void* resp) {
+
+ WAKU_CALL (waku_get_peerids_by_protocol(wakuCtx,
+ protocol,
+ (WakuCallBack) callback,
+ resp));
+ }
+
+*/
+import "C"
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+ "os/signal"
+ "syscall"
+ "unsafe"
+)
+
+type WakuMessageHash = string
+type WakuPubsubTopic = string
+type WakuContentTopic = string
+
+type WakuConfig struct {
+ Host string `json:"host,omitempty"`
+ Port int `json:"port,omitempty"`
+ NodeKey string `json:"key,omitempty"`
+ EnableRelay bool `json:"relay"`
+ LogLevel string `json:"logLevel"`
+}
+
+type WakuNode struct {
+ ctx unsafe.Pointer
+}
+
+func WakuNew(config WakuConfig) (*WakuNode, error) {
+ jsonConfig, err := json.Marshal(config)
+ if err != nil {
+ return nil, err
+ }
+
+ var cJsonConfig = C.CString(string(jsonConfig))
+ var resp = C.allocResp()
+
+ defer C.free(unsafe.Pointer(cJsonConfig))
+ defer C.freeResp(resp)
+
+ ctx := C.cGoWakuNew(cJsonConfig, resp)
+ if C.getRet(resp) == C.RET_OK {
+ return &WakuNode{ctx: ctx}, nil
+ }
+
+ errMsg := "error WakuNew: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return nil, errors.New(errMsg)
+}
+
+func (self *WakuNode) WakuStart() error {
+ var resp = C.allocResp()
+ defer C.freeResp(resp)
+ C.cGoWakuStart(self.ctx, resp)
+
+ if C.getRet(resp) == C.RET_OK {
+ return nil
+ }
+ errMsg := "error WakuStart: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return errors.New(errMsg)
+}
+
+func (self *WakuNode) WakuStop() error {
+ var resp = C.allocResp()
+ defer C.freeResp(resp)
+ C.cGoWakuStop(self.ctx, resp)
+
+ if C.getRet(resp) == C.RET_OK {
+ return nil
+ }
+ errMsg := "error WakuStop: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return errors.New(errMsg)
+}
+
+func (self *WakuNode) WakuDestroy() error {
+ var resp = C.allocResp()
+ defer C.freeResp(resp)
+ C.cGoWakuDestroy(self.ctx, resp)
+
+ if C.getRet(resp) == C.RET_OK {
+ return nil
+ }
+ errMsg := "error WakuDestroy: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return errors.New(errMsg)
+}
+
+func (self *WakuNode) WakuVersion() (string, error) {
+ var resp = C.allocResp()
+ defer C.freeResp(resp)
+
+ C.cGoWakuVersion(self.ctx, resp)
+
+ if C.getRet(resp) == C.RET_OK {
+ var version = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return version, nil
+ }
+
+ errMsg := "error WakuVersion: " +
+ C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return "", errors.New(errMsg)
+}
+
+//export globalEventCallback
+func globalEventCallback(callerRet C.int, msg *C.char, len C.size_t, userData unsafe.Pointer) {
+ // This is shared among all Golang instances
+
+ self := WakuNode{ctx: userData}
+ self.MyEventCallback(callerRet, msg, len)
+}
+
+func (self *WakuNode) MyEventCallback(callerRet C.int, msg *C.char, len C.size_t) {
+ fmt.Println("Event received:", C.GoStringN(msg, C.int(len)))
+}
+
+func (self *WakuNode) WakuSetEventCallback() {
+ // Notice that the events for self node are handled by the 'MyEventCallback' method
+ C.cGoWakuSetEventCallback(self.ctx)
+}
+
+func (self *WakuNode) FormatContentTopic(
+ appName string,
+ appVersion int,
+ contentTopicName string,
+ encoding string) (WakuContentTopic, error) {
+
+ var cAppName = C.CString(appName)
+ var cContentTopicName = C.CString(contentTopicName)
+ var cEncoding = C.CString(encoding)
+ var resp = C.allocResp()
+
+ defer C.free(unsafe.Pointer(cAppName))
+ defer C.free(unsafe.Pointer(cContentTopicName))
+ defer C.free(unsafe.Pointer(cEncoding))
+ defer C.freeResp(resp)
+
+ C.cGoWakuContentTopic(self.ctx,
+ cAppName,
+ C.int(appVersion),
+ cContentTopicName,
+ cEncoding,
+ resp)
+
+ if C.getRet(resp) == C.RET_OK {
+ var contentTopic = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return contentTopic, nil
+ }
+
+ errMsg := "error FormatContentTopic: " +
+ C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+
+ return "", errors.New(errMsg)
+}
+
+func (self *WakuNode) FormatPubsubTopic(topicName string) (WakuPubsubTopic, error) {
+ var cTopicName = C.CString(topicName)
+ var resp = C.allocResp()
+
+ defer C.free(unsafe.Pointer(cTopicName))
+ defer C.freeResp(resp)
+
+ C.cGoWakuPubsubTopic(self.ctx, cTopicName, resp)
+ if C.getRet(resp) == C.RET_OK {
+ var pubsubTopic = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return pubsubTopic, nil
+ }
+
+ errMsg := "error FormatPubsubTopic: " +
+ C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+
+ return "", errors.New(errMsg)
+}
+
+func (self *WakuNode) WakuDefaultPubsubTopic() (WakuPubsubTopic, error) {
+ var resp = C.allocResp()
+ defer C.freeResp(resp)
+ C.cGoWakuDefaultPubsubTopic(self.ctx, resp)
+ if C.getRet(resp) == C.RET_OK {
+ var defaultPubsubTopic = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return defaultPubsubTopic, nil
+ }
+
+ errMsg := "error WakuDefaultPubsubTopic: " +
+ C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+
+ return "", errors.New(errMsg)
+}
+
+func (self *WakuNode) WakuRelayPublish(
+ pubsubTopic string,
+ message string,
+ timeoutMs int) (WakuMessageHash, error) {
+
+ var cPubsubTopic = C.CString(pubsubTopic)
+ var msg = C.CString(message)
+ var resp = C.allocResp()
+
+ defer C.freeResp(resp)
+ defer C.free(unsafe.Pointer(cPubsubTopic))
+ defer C.free(unsafe.Pointer(msg))
+
+ C.cGoWakuRelayPublish(self.ctx, cPubsubTopic, msg, C.int(timeoutMs), resp)
+ if C.getRet(resp) == C.RET_OK {
+ msgHash := C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return msgHash, nil
+ }
+ errMsg := "error WakuRelayPublish: " +
+ C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return "", errors.New(errMsg)
+}
+
+func (self *WakuNode) WakuRelaySubscribe(pubsubTopic string) error {
+ var resp = C.allocResp()
+ var cPubsubTopic = C.CString(pubsubTopic)
+
+ defer C.freeResp(resp)
+ defer C.free(unsafe.Pointer(cPubsubTopic))
+ C.cGoWakuRelaySubscribe(self.ctx, cPubsubTopic, resp)
+
+ if C.getRet(resp) == C.RET_OK {
+ return nil
+ }
+ errMsg := "error WakuRelaySubscribe: " +
+ C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return errors.New(errMsg)
+}
+
+func (self *WakuNode) WakuRelayUnsubscribe(pubsubTopic string) error {
+ var resp = C.allocResp()
+ var cPubsubTopic = C.CString(pubsubTopic)
+ defer C.freeResp(resp)
+ defer C.free(unsafe.Pointer(cPubsubTopic))
+ C.cGoWakuRelayUnsubscribe(self.ctx, cPubsubTopic, resp)
+
+ if C.getRet(resp) == C.RET_OK {
+ return nil
+ }
+ errMsg := "error WakuRelayUnsubscribe: " +
+ C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return errors.New(errMsg)
+}
+
+func (self *WakuNode) WakuConnect(peerMultiAddr string, timeoutMs int) error {
+ var resp = C.allocResp()
+ var cPeerMultiAddr = C.CString(peerMultiAddr)
+ defer C.freeResp(resp)
+ defer C.free(unsafe.Pointer(cPeerMultiAddr))
+
+ C.cGoWakuConnect(self.ctx, cPeerMultiAddr, C.int(timeoutMs), resp)
+
+ if C.getRet(resp) == C.RET_OK {
+ return nil
+ }
+ errMsg := "error WakuConnect: " +
+ C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return errors.New(errMsg)
+}
+
+func (self *WakuNode) WakuListenAddresses() (string, error) {
+ var resp = C.allocResp()
+ defer C.freeResp(resp)
+ C.cGoWakuListenAddresses(self.ctx, resp)
+
+ if C.getRet(resp) == C.RET_OK {
+ var listenAddresses = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return listenAddresses, nil
+ }
+ errMsg := "error WakuListenAddresses: " +
+ C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return "", errors.New(errMsg)
+}
+
+func (self *WakuNode) WakuGetMyENR() (string, error) {
+ var resp = C.allocResp()
+ defer C.freeResp(resp)
+ C.cGoWakuGetMyENR(self.ctx, resp)
+
+ if C.getRet(resp) == C.RET_OK {
+ var myENR = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return myENR, nil
+ }
+ errMsg := "error WakuGetMyENR: " +
+ C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp)))
+ return "", errors.New(errMsg)
+}
+
+func main() {
+ config := WakuConfig{
+ Host: "0.0.0.0",
+ Port: 30304,
+ NodeKey: "11d0dcea28e86f81937a3bd1163473c7fbc0a0db54fd72914849bc47bdf78710",
+ EnableRelay: true,
+ LogLevel: "DEBUG",
+ }
+
+ node, err := WakuNew(config)
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+
+ node.WakuSetEventCallback()
+
+ defaultPubsubTopic, err := node.WakuDefaultPubsubTopic()
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+
+ err = node.WakuRelaySubscribe(defaultPubsubTopic)
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+
+ err = node.WakuConnect(
+ // tries to connect to a localhost node with key: 0d714a1fada214dead6dc9c7274585eca0ff292451866e7d6d677dc818e8ccd2
+ "/ip4/0.0.0.0/tcp/60000/p2p/16Uiu2HAmVFXtAfSj4EiR7mL2KvL4EE2wztuQgUSBoj2Jx2KeXFLN",
+ 10000)
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+
+ err = node.WakuStart()
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+
+ version, err := node.WakuVersion()
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+
+ formattedContentTopic, err := node.FormatContentTopic("appName", 1, "cTopicName", "enc")
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+
+ formattedPubsubTopic, err := node.FormatPubsubTopic("my-ctopic")
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+
+ listenAddresses, err := node.WakuListenAddresses()
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+
+ myENR, err := node.WakuGetMyENR()
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+
+ fmt.Println("Version:", version)
+ fmt.Println("Custom content topic:", formattedContentTopic)
+ fmt.Println("Custom pubsub topic:", formattedPubsubTopic)
+ fmt.Println("Default pubsub topic:", defaultPubsubTopic)
+ fmt.Println("Listen addresses:", listenAddresses)
+ fmt.Println("My ENR:", myENR)
+
+ // Wait for a SIGINT or SIGTERM signal
+ ch := make(chan os.Signal, 1)
+ signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
+ <-ch
+
+ err = node.WakuStop()
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+
+ err = node.WakuDestroy()
+ if err != nil {
+ fmt.Println("Error happened:", err.Error())
+ return
+ }
+}
diff --git a/third-party/nwaku/examples/lightpush_mix/lightpush_publisher_mix.nim b/third-party/nwaku/examples/lightpush_mix/lightpush_publisher_mix.nim
new file mode 100644
index 0000000..26c0a06
--- /dev/null
+++ b/third-party/nwaku/examples/lightpush_mix/lightpush_publisher_mix.nim
@@ -0,0 +1,196 @@
+import
+ std/[tables, times, sequtils, strutils],
+ stew/byteutils,
+ chronicles,
+ results,
+ chronos,
+ confutils,
+ libp2p/crypto/crypto,
+ libp2p/crypto/curve25519,
+ libp2p/multiaddress,
+ eth/keys,
+ eth/p2p/discoveryv5/enr,
+ metrics,
+ metrics/chronos_httpserver
+
+import mix, mix/mix_protocol, mix/curve25519
+
+import
+ waku/[
+ common/logging,
+ node/peer_manager,
+ waku_core,
+ waku_core/codecs,
+ waku_node,
+ waku_enr,
+ discovery/waku_discv5,
+ factory/builder,
+ waku_lightpush/client,
+ ],
+ ./lightpush_publisher_mix_config,
+ ./lightpush_publisher_mix_metrics
+
+const clusterId = 66
+const shardId = @[0'u16]
+
+const
+ LightpushPubsubTopic = PubsubTopic("/waku/2/rs/66/0")
+ LightpushContentTopic = ContentTopic("/examples/1/light-pubsub-mix-example/proto")
+
+proc splitPeerIdAndAddr(maddr: string): (string, string) =
+ let parts = maddr.split("/p2p/")
+ if parts.len != 2:
+ error "Invalid multiaddress format", parts = parts
+ return
+
+ let
+ address = parts[0]
+ peerId = parts[1]
+ return (address, peerId)
+
+proc setupAndPublish(rng: ref HmacDrbgContext, conf: LightPushMixConf) {.async.} =
+ # use notice to filter all waku messaging
+ setupLog(logging.LogLevel.DEBUG, logging.LogFormat.TEXT)
+
+ notice "starting publisher", wakuPort = conf.port
+
+ let
+ nodeKey = crypto.PrivateKey.random(Secp256k1, rng[]).get()
+ ip = parseIpAddress("0.0.0.0")
+ flags = CapabilitiesBitfield.init(relay = true)
+
+ let relayShards = RelayShards.init(clusterId, shardId).valueOr:
+ error "Relay shards initialization failed", error = error
+ quit(QuitFailure)
+
+ var enrBuilder = EnrBuilder.init(nodeKey)
+ enrBuilder.withWakuRelaySharding(relayShards).expect(
+ "Building ENR with relay sharding failed"
+ )
+
+ let record = enrBuilder.build().valueOr:
+ error "failed to create enr record", error = error
+ quit(QuitFailure)
+
+ setLogLevel(logging.LogLevel.TRACE)
+ var builder = WakuNodeBuilder.init()
+ builder.withNodeKey(nodeKey)
+ builder.withRecord(record)
+ builder.withNetworkConfigurationDetails(ip, Port(conf.port)).tryGet()
+
+ let node = builder.build().tryGet()
+
+ node.mountMetadata(clusterId, shardId).expect(
+ "failed to mount waku metadata protocol"
+ )
+ node.mountLightPushClient()
+ try:
+ await node.mountPeerExchange(some(uint16(clusterId)))
+ except CatchableError:
+ error "failed to mount waku peer-exchange protocol",
+ error = getCurrentExceptionMsg()
+ return
+
+ let (destPeerAddr, destPeerId) = splitPeerIdAndAddr(conf.destPeerAddr)
+ let (pxPeerAddr, pxPeerId) = splitPeerIdAndAddr(conf.pxAddr)
+ info "dest peer address", destPeerAddr = destPeerAddr, destPeerId = destPeerId
+ info "peer exchange address", pxPeerAddr = pxPeerAddr, pxPeerId = pxPeerId
+ let pxPeerInfo =
+ RemotePeerInfo.init(destPeerId, @[MultiAddress.init(destPeerAddr).get()])
+ node.peerManager.addServicePeer(pxPeerInfo, WakuPeerExchangeCodec)
+
+ let pxPeerInfo1 =
+ RemotePeerInfo.init(pxPeerId, @[MultiAddress.init(pxPeerAddr).get()])
+ node.peerManager.addServicePeer(pxPeerInfo1, WakuPeerExchangeCodec)
+
+ if not conf.mixDisabled:
+ let (mixPrivKey, mixPubKey) = generateKeyPair().valueOr:
+ error "failed to generate mix key pair", error = error
+ return
+ (await node.mountMix(clusterId, mixPrivKey)).isOkOr:
+ error "failed to mount waku mix protocol: ", error = $error
+ return
+
+ let dPeerId = PeerId.init(destPeerId).valueOr:
+ error "Failed to initialize PeerId", error = error
+ return
+ var conn: Connection
+ if not conf.mixDisabled:
+ conn = node.wakuMix.toConnection(
+ MixDestination.init(dPeerId, pxPeerInfo.addrs[0]), # destination lightpush peer
+ WakuLightPushCodec, # protocol codec which will be used over the mix connection
+ Opt.some(MixParameters(expectReply: Opt.some(true), numSurbs: Opt.some(byte(1)))),
+ # mix parameters indicating we expect a single reply
+ ).valueOr:
+ error "failed to create mix connection", error = error
+ return
+
+ await node.start()
+ node.peerManager.start()
+ node.startPeerExchangeLoop()
+ try:
+ startMetricsHttpServer("0.0.0.0", Port(8008))
+ except Exception:
+ error "failed to start metrics server: ", error = getCurrentExceptionMsg()
+ (await node.fetchPeerExchangePeers()).isOkOr:
+ warn "Cannot fetch peers from peer exchange", cause = error
+
+ if not conf.mixDisabled:
+ while node.getMixNodePoolSize() < conf.minMixPoolSize:
+ info "waiting for mix nodes to be discovered",
+ currentpoolSize = node.getMixNodePoolSize()
+ await sleepAsync(1000)
+ notice "publisher service started with mix node pool size ",
+ currentpoolSize = node.getMixNodePoolSize()
+
+ var i = 0
+ while i < conf.numMsgs:
+ if conf.mixDisabled:
+ let connOpt = await node.peerManager.dialPeer(dPeerId, WakuLightPushCodec)
+ if connOpt.isNone():
+ error "failed to dial peer with WakuLightPushCodec", target_peer_id = dPeerId
+ return
+ conn = connOpt.get()
+ i = i + 1
+ let text =
+ """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam venenatis magna ut tortor faucibus, in vestibulum nibh commodo. Aenean eget vestibulum augue. Nullam suscipit urna non nunc efficitur, at iaculis nisl consequat. Mauris quis ultrices elit. Suspendisse lobortis odio vitae laoreet facilisis. Cras ornare sem felis, at vulputate magna aliquam ac. Duis quis est ultricies, euismod nulla ac, interdum dui. Maecenas sit amet est vitae enim commodo gravida. Proin vitae elit nulla. Donec tempor dolor lectus, in faucibus velit elementum quis. Donec non mauris eu nibh faucibus cursus ut egestas dolor. Aliquam venenatis ligula id velit pulvinar malesuada. Vestibulum scelerisque, justo non porta gravida, nulla justo tempor purus, at sollicitudin erat erat vel libero.
+ Fusce nec eros eu metus tristique aliquet. Sed ut magna sagittis, vulputate diam sit amet, aliquam magna. Aenean sollicitudin velit lacus, eu ultrices magna semper at. Integer vitae felis ligula. In a eros nec risus condimentum tincidunt fermentum sit amet ex. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nullam vitae justo maximus, fringilla tellus nec, rutrum purus. Etiam efficitur nisi dapibus euismod vestibulum. Phasellus at felis elementum, tristique nulla ac, consectetur neque.
+ Maecenas hendrerit nibh eget velit rutrum, in ornare mauris molestie. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Praesent dignissim efficitur eros, sit amet rutrum justo mattis a. Fusce mollis neque at erat placerat bibendum. Ut fringilla fringilla orci, ut fringilla metus fermentum vel. In hac habitasse platea dictumst. Donec hendrerit porttitor odio. Suspendisse ornare sollicitudin mauris, sodales pulvinar velit finibus vel. Fusce id pulvinar neque. Suspendisse eget tincidunt sapien, ac accumsan turpis.
+ Curabitur cursus tincidunt leo at aliquet. Nunc dapibus quam id venenatis varius. Aenean eget augue vel velit dapibus aliquam. Nulla facilisi. Curabitur cursus, turpis vel congue volutpat, tellus eros cursus lacus, eu fringilla turpis orci non ipsum. In hac habitasse platea dictumst. Nulla aliquam nisl a nunc placerat, eget dignissim felis pulvinar. Fusce sed porta mauris. Donec sodales arcu in nisl sodales, quis posuere massa ultricies. Nam feugiat massa eget felis ultricies finibus. Nunc magna nulla, interdum a elit vel, egestas efficitur urna. Ut posuere tincidunt odio in maximus. Sed at dignissim est.
+ Morbi accumsan elementum ligula ut fringilla. Praesent in ex metus. Phasellus urna est, tempus sit amet elementum vitae, sollicitudin vel ipsum. Fusce hendrerit eleifend dignissim. Maecenas tempor dapibus dui quis laoreet. Cras tincidunt sed ipsum sed pellentesque. Proin ut tellus nec ipsum varius interdum. Curabitur id velit ligula. Etiam sapien nulla, cursus sodales orci eu, porta lobortis nunc. Nunc at dapibus velit. Nulla et nunc vehicula, condimentum erat quis, elementum dolor. Quisque eu metus fermentum, vestibulum tellus at, sollicitudin odio. Ut vel neque justo.
+ Praesent porta porta velit, vel porttitor sem. Donec sagittis at nulla venenatis iaculis. Nullam vel eleifend felis. Nullam a pellentesque lectus. Aliquam tincidunt semper dui sed bibendum. Donec hendrerit, urna et cursus dictum, neque neque convallis magna, id condimentum sem urna quis massa. Fusce non quam vulputate, fermentum mauris at, malesuada ipsum. Mauris id pellentesque libero. Donec vel erat ullamcorper, dapibus quam id, imperdiet urna. Praesent sed ligula ut est pellentesque pharetra quis et diam. Ut placerat lorem eget mi fermentum aliquet.
+ This is message #""" &
+ $i & """ sent from a publisher using mix. End of transmission."""
+ let message = WakuMessage(
+ payload: toBytes(text), # content of the message
+ contentTopic: LightpushContentTopic, # content topic to publish to
+ ephemeral: true, # tell store nodes to not store it
+ timestamp: getNowInNanosecondTime(),
+ ) # current timestamp
+
+ let res = await node.wakuLightpushClient.publishWithConn(
+ LightpushPubsubTopic, message, conn, dPeerId
+ )
+
+ if res.isOk():
+ lp_mix_success.inc()
+ notice "published message",
+ text = text,
+ timestamp = message.timestamp,
+ psTopic = LightpushPubsubTopic,
+ contentTopic = LightpushContentTopic
+ else:
+ error "failed to publish message", error = $res.error
+ lp_mix_failed.inc(labelValues = ["publish_error"])
+
+ if conf.mixDisabled:
+ await conn.close()
+ await sleepAsync(conf.msgIntervalMilliseconds)
+ info "###########Sent all messages via mix"
+ quit(0)
+
+when isMainModule:
+ let conf = LightPushMixConf.load()
+ let rng = crypto.newRng()
+ asyncSpawn setupAndPublish(rng, conf)
+ runForever()
diff --git a/third-party/nwaku/examples/lightpush_mix/lightpush_publisher_mix_config.nim b/third-party/nwaku/examples/lightpush_mix/lightpush_publisher_mix_config.nim
new file mode 100644
index 0000000..7a135e3
--- /dev/null
+++ b/third-party/nwaku/examples/lightpush_mix/lightpush_publisher_mix_config.nim
@@ -0,0 +1,28 @@
+import confutils/defs
+
+type LightPushMixConf* = object
+ destPeerAddr* {.desc: "Destination peer address with peerId.", name: "dp-addr".}:
+ string
+
+ pxAddr* {.desc: "Peer exchange address with peerId.", name: "px-addr".}: string
+
+ port* {.desc: "Port to listen on.", defaultValue: 50000, name: "port".}: int
+
+ numMsgs* {.desc: "Number of messages to send.", defaultValue: 1, name: "num-msgs".}:
+ int
+
+ msgIntervalMilliseconds* {.
+ desc: "Interval between messages in milliseconds.",
+ defaultValue: 1000,
+ name: "msg-interval"
+ .}: int
+
+ minMixPoolSize* {.
+ desc: "Number of mix nodes to be discovered before sending lightpush messages.",
+ defaultValue: 3,
+ name: "min-mix-pool-size"
+ .}: int
+
+ mixDisabled* {.
+ desc: "Do not use mix for publishing.", defaultValue: false, name: "without-mix"
+ .}: bool
diff --git a/third-party/nwaku/examples/lightpush_mix/lightpush_publisher_mix_metrics.nim b/third-party/nwaku/examples/lightpush_mix/lightpush_publisher_mix_metrics.nim
new file mode 100644
index 0000000..cd06b3e
--- /dev/null
+++ b/third-party/nwaku/examples/lightpush_mix/lightpush_publisher_mix_metrics.nim
@@ -0,0 +1,8 @@
+{.push raises: [].}
+
+import metrics
+
+declarePublicCounter lp_mix_success, "number of lightpush messages sent via mix"
+
+declarePublicCounter lp_mix_failed,
+ "number of lightpush messages failed via mix", labels = ["error"]
diff --git a/third-party/nwaku/examples/lightpush_publisher.nim b/third-party/nwaku/examples/lightpush_publisher.nim
new file mode 100644
index 0000000..e9fa217
--- /dev/null
+++ b/third-party/nwaku/examples/lightpush_publisher.nim
@@ -0,0 +1,109 @@
+import
+ std/[tables, times, sequtils],
+ stew/byteutils,
+ chronicles,
+ results,
+ chronos,
+ confutils,
+ libp2p/crypto/crypto,
+ eth/keys,
+ eth/p2p/discoveryv5/enr
+
+import
+ waku/[
+ common/logging,
+ node/peer_manager,
+ waku_core,
+ waku_node,
+ waku_enr,
+ discovery/waku_discv5,
+ factory/builder,
+ ]
+
+proc now*(): Timestamp =
+ getNanosecondTime(getTime().toUnixFloat())
+
+# careful if running pub and sub in the same machine
+const wakuPort = 60000
+
+const clusterId = 1
+const shardId = @[0'u16]
+
+const
+ LightpushPeer =
+ "/ip4/64.225.80.192/tcp/30303/p2p/16Uiu2HAmNaeL4p3WEYzC9mgXBmBWSgWjPHRvatZTXnp8Jgv3iKsb"
+ LightpushPubsubTopic = PubsubTopic("/waku/2/rs/1/0")
+ LightpushContentTopic = ContentTopic("/examples/1/light-pubsub-example/proto")
+
+proc setupAndPublish(rng: ref HmacDrbgContext) {.async.} =
+ # use notice to filter all waku messaging
+ setupLog(logging.LogLevel.NOTICE, logging.LogFormat.TEXT)
+
+ notice "starting publisher", wakuPort = wakuPort
+ let
+ nodeKey = crypto.PrivateKey.random(Secp256k1, rng[]).get()
+ ip = parseIpAddress("0.0.0.0")
+ flags = CapabilitiesBitfield.init(relay = true)
+
+ let relayShards = RelayShards.init(clusterId, shardId).valueOr:
+ error "Relay shards initialization failed", error = error
+ quit(QuitFailure)
+
+ var enrBuilder = EnrBuilder.init(nodeKey)
+ enrBuilder.withWakuRelaySharding(relayShards).expect(
+ "Building ENR with relay sharding failed"
+ )
+
+ let recordRes = enrBuilder.build()
+ let record =
+ if recordRes.isErr():
+ error "failed to create enr record", error = recordRes.error
+ quit(QuitFailure)
+ else:
+ recordRes.get()
+
+ var builder = WakuNodeBuilder.init()
+ builder.withNodeKey(nodeKey)
+ builder.withRecord(record)
+ builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet()
+ let node = builder.build().tryGet()
+
+ node.mountMetadata(clusterId, shardId).expect(
+ "failed to mount waku metadata protocol"
+ )
+ node.mountLegacyLightPushClient()
+
+ await node.start()
+ node.peerManager.start()
+
+ notice "publisher service started"
+ while true:
+ let text = "hi there i'm a publisher"
+ let message = WakuMessage(
+ payload: toBytes(text), # content of the message
+ contentTopic: LightpushContentTopic, # content topic to publish to
+ ephemeral: true, # tell store nodes to not store it
+ timestamp: now(),
+ ) # current timestamp
+
+ let lightpushPeer = parsePeerInfo(LightpushPeer).get()
+
+ let res = await node.legacyLightpushPublish(
+ some(LightpushPubsubTopic), message, lightpushPeer
+ )
+
+ if res.isOk:
+ notice "published message",
+ text = text,
+ timestamp = message.timestamp,
+ psTopic = LightpushPubsubTopic,
+ contentTopic = LightpushContentTopic
+ else:
+ error "failed to publish message", error = res.error
+
+ await sleepAsync(5000)
+
+when isMainModule:
+ let rng = crypto.newRng()
+ asyncSpawn setupAndPublish(rng)
+ runForever()
diff --git a/third-party/nwaku/examples/mobile/.bundle/config b/third-party/nwaku/examples/mobile/.bundle/config
new file mode 100644
index 0000000..848943b
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/.bundle/config
@@ -0,0 +1,2 @@
+BUNDLE_PATH: "vendor/bundle"
+BUNDLE_FORCE_RUBY_PLATFORM: 1
diff --git a/third-party/nwaku/examples/mobile/.eslintrc.js b/third-party/nwaku/examples/mobile/.eslintrc.js
new file mode 100644
index 0000000..187894b
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/.eslintrc.js
@@ -0,0 +1,4 @@
+module.exports = {
+ root: true,
+ extends: '@react-native',
+};
diff --git a/third-party/nwaku/examples/mobile/.gitignore b/third-party/nwaku/examples/mobile/.gitignore
new file mode 100644
index 0000000..d5ae456
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/.gitignore
@@ -0,0 +1,74 @@
+# OSX
+#
+.DS_Store
+
+# Xcode
+#
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata
+*.xccheckout
+*.moved-aside
+DerivedData
+*.hmap
+*.ipa
+*.xcuserstate
+**/.xcode.env.local
+
+# Android/IntelliJ
+#
+build/
+.idea
+.gradle
+local.properties
+*.iml
+*.hprof
+.cxx/
+*.keystore
+!debug.keystore
+
+# node.js
+#
+node_modules/
+npm-debug.log
+yarn-error.log
+
+# fastlane
+#
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
+# screenshots whenever they are needed.
+# For more information about the recommended setup visit:
+# https://docs.fastlane.tools/best-practices/source-control/
+
+**/fastlane/report.xml
+**/fastlane/Preview.html
+**/fastlane/screenshots
+**/fastlane/test_output
+
+# Bundle artifact
+*.jsbundle
+
+# Ruby / CocoaPods
+**/Pods/
+/vendor/bundle/
+
+# Temporary files created by Metro to check the health of the file watcher
+.metro-health-check*
+
+# testing
+/coverage
+
+# Yarn
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/sdks
+!.yarn/versions
diff --git a/third-party/nwaku/examples/mobile/.prettierrc.js b/third-party/nwaku/examples/mobile/.prettierrc.js
new file mode 100644
index 0000000..2b54074
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/.prettierrc.js
@@ -0,0 +1,7 @@
+module.exports = {
+ arrowParens: 'avoid',
+ bracketSameLine: true,
+ bracketSpacing: false,
+ singleQuote: true,
+ trailingComma: 'all',
+};
diff --git a/third-party/nwaku/examples/mobile/.watchmanconfig b/third-party/nwaku/examples/mobile/.watchmanconfig
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/.watchmanconfig
@@ -0,0 +1 @@
+{}
diff --git a/third-party/nwaku/examples/mobile/.yarnrc b/third-party/nwaku/examples/mobile/.yarnrc
new file mode 100644
index 0000000..85b738b
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/.yarnrc
@@ -0,0 +1,5 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+yarn-path ".yarn/releases/yarn-1.22.22.cjs"
diff --git a/third-party/nwaku/examples/mobile/App.tsx b/third-party/nwaku/examples/mobile/App.tsx
new file mode 100644
index 0000000..bf01de3
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/App.tsx
@@ -0,0 +1,327 @@
+/* eslint-disable no-alert */
+/**
+ * Sample React Native App
+ * https://github.com/facebook/react-native
+ *
+ * @format
+ */
+
+import React from 'react';
+import type {PropsWithChildren} from 'react';
+import {
+ SafeAreaView,
+ ScrollView,
+ StatusBar,
+ StyleSheet,
+ Text,
+ useColorScheme,
+ View,
+} from 'react-native';
+
+import {Colors} from 'react-native/Libraries/NewAppScreen';
+import {
+ NativeEventEmitter,
+ NativeModules,
+ EmitterSubscription,
+ Button,
+} from 'react-native';
+
+type SectionProps = PropsWithChildren<{
+ title: string;
+}>;
+
+function Section({children, title}: SectionProps): React.JSX.Element {
+ const isDarkMode = useColorScheme() === 'dark';
+ return (
+
+
+ {title}
+
+
+ {children}
+
+
+ );
+}
+
+const WakuFactory = (() => {
+ let isSetup = false;
+
+ const eventEmitter = new NativeEventEmitter(NativeModules.WakuModule);
+ class Waku {
+ wakuPtr: Number;
+
+ constructor(wakuPtr: Number) {
+ this.wakuPtr = wakuPtr;
+ }
+
+ async destroy(): Promise {
+ await NativeModules.WakuModule.destroy(this.wakuPtr);
+ }
+
+ async start(): Promise {
+ return NativeModules.WakuModule.start(this.wakuPtr);
+ }
+
+ async stop(): Promise {
+ return NativeModules.WakuModule.stop(this.wakuPtr);
+ }
+
+ async version(): Promise {
+ return NativeModules.WakuModule.version(this.wakuPtr);
+ }
+
+ async listenAddresses(): Promise> {
+ let addresses = await NativeModules.WakuModule.listenAddresses(
+ this.wakuPtr,
+ );
+ return addresses;
+ }
+
+ async connect(peerMultiaddr: String, timeoutMs: Number): Promise {
+ return NativeModules.WakuModule.connect(
+ this.wakuPtr,
+ peerMultiaddr,
+ timeoutMs,
+ );
+ }
+
+ async relaySubscribe(pubsubTopic: String): Promise {
+ return NativeModules.WakuModule.relaySubscribe(this.wakuPtr, pubsubTopic);
+ }
+
+ async relayUnsubscribe(pubsubTopic: String): Promise {
+ return NativeModules.WakuModule.relayUnsubscribe(
+ this.wakuPtr,
+ pubsubTopic,
+ );
+ }
+
+ // TODO: Use a type instead of `any`
+ async relayPublish(
+ pubsubTopic: string,
+ msg: any,
+ timeoutMs: Number,
+ ): Promise {
+ return NativeModules.WakuModule.relayPublish(
+ this.wakuPtr,
+ pubsubTopic,
+ msg,
+ timeoutMs,
+ );
+ }
+
+ onEvent(cb: (event: any) => void): EmitterSubscription {
+ return eventEmitter.addListener('wakuEvent', evt => {
+ if (evt.wakuPtr === this.wakuPtr) {
+ cb(JSON.parse(evt.event));
+ }
+ });
+ }
+ }
+
+ async function createInstance(config: any) {
+ if (!isSetup) {
+ console.debug('initializing waku library');
+ await NativeModules.WakuModule.setup();
+ isSetup = true;
+ alert('waku instance created!');
+ }
+
+ let wakuPtr = await NativeModules.WakuModule.new(config);
+ return new Waku(wakuPtr);
+ }
+
+ // Expose the factory method
+ return {
+ createInstance,
+ Waku,
+ };
+})();
+
+function App(): React.JSX.Element {
+ const isDarkMode = useColorScheme() === 'dark';
+
+ const backgroundStyle = {
+ backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
+ };
+
+ var waku: Waku;
+
+ const onClickNew = async () => {
+ const config = {
+ host: '0.0.0.0',
+ port: 42342,
+ key: '1122334455667788990011223344556677889900112233445566778899000022',
+ relay: true,
+ };
+ waku = await WakuFactory.createInstance(config);
+ };
+
+ const onClickStart = async () => {
+ await waku.start();
+ alert('start executed succesfully');
+ };
+
+ const onClickVersion = async () => {
+ let version = await waku.version();
+ alert(version);
+ };
+
+ const onClickListenAddresses = async () => {
+ let addresses = await waku.listenAddresses();
+ alert(addresses[0]);
+ };
+
+ const onClickStop = async () => {
+ await waku.stop();
+ alert('stopped!');
+ };
+
+ const onClickDestroy = async () => {
+ await waku.destroy();
+ alert('destroyed!');
+ };
+
+ const onClickConnect = async () => {
+ let result = await waku.connect(
+ '/ip4/127.0.0.1/tcp/48117/p2p/16Uiu2HAmVrsyU3y3pQYuSEyaqrBgevQeshp7YZsL8rY3nWb2yWD5',
+ 0,
+ );
+ alert(
+ 'connected? (TODO: bindings function do not return connection attempt status)',
+ );
+ };
+
+ const onClickSubscribe = async () => {
+ await waku.relaySubscribe('test');
+ alert('subscribed to test');
+ };
+
+ const onClickUnsubscribe = async () => {
+ await waku.relayUnsubscribe('test');
+ alert('unsubscribed from test');
+ };
+
+ const onClickSetEventCallback = async () => {
+ const eventSubs = waku.onEvent((event: any) => {
+ console.log(event);
+ alert('received a message');
+ });
+ // TODO: eventSubs.remove() should be used to avoid a mem leak.
+
+ alert("event callback set");
+ };
+
+ const onClickPublish = async () => {
+ const pubsubTopic = 'test';
+ const msg = {
+ payload: 'aGVsbG8',
+ contentTopic: 'test',
+ timestamp: 0,
+ version: 0,
+ };
+ let hash = await waku.relayPublish(pubsubTopic, msg, 0);
+ alert('published - msgHash: ' + hash);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ sectionContainer: {
+ marginTop: 32,
+ paddingHorizontal: 24,
+ },
+ sectionTitle: {
+ fontSize: 24,
+ fontWeight: '600',
+ },
+ sectionDescription: {
+ marginTop: 8,
+ fontSize: 18,
+ fontWeight: '400',
+ },
+ highlight: {
+ fontWeight: '700',
+ },
+});
+
+export default App;
diff --git a/third-party/nwaku/examples/mobile/Gemfile b/third-party/nwaku/examples/mobile/Gemfile
new file mode 100644
index 0000000..8d72c37
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/Gemfile
@@ -0,0 +1,9 @@
+source 'https://rubygems.org'
+
+# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
+ruby ">= 2.6.10"
+
+# Cocoapods 1.15 introduced a bug which break the build. We will remove the upper
+# bound in the template on Cocoapods with next React Native release.
+gem 'cocoapods', '>= 1.13', '< 1.15'
+gem 'activesupport', '>= 6.1.7.5', '< 7.1.0'
diff --git a/third-party/nwaku/examples/mobile/README.md b/third-party/nwaku/examples/mobile/README.md
new file mode 100644
index 0000000..12470c3
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/README.md
@@ -0,0 +1,79 @@
+This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli).
+
+# Getting Started
+
+>**Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding.
+
+## Step 1: Start the Metro Server
+
+First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native.
+
+To start Metro, run the following command from the _root_ of your React Native project:
+
+```bash
+# using npm
+npm start
+
+# OR using Yarn
+yarn start
+```
+
+## Step 2: Start your Application
+
+Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app:
+
+### For Android
+
+```bash
+# using npm
+npm run android
+
+# OR using Yarn
+yarn android
+```
+
+### For iOS
+
+```bash
+# using npm
+npm run ios
+
+# OR using Yarn
+yarn ios
+```
+
+If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly.
+
+This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively.
+
+## Step 3: Modifying your App
+
+Now that you have successfully run the app, let's modify it.
+
+1. Open `App.tsx` in your text editor of choice and edit some lines.
+2. For **Android**: Press the R key twice or select **"Reload"** from the **Developer Menu** (Ctrl + M (on Window and Linux) or Cmd ⌘ + M (on macOS)) to see your changes!
+
+ For **iOS**: Hit Cmd ⌘ + R in your iOS Simulator to reload the app and see your changes!
+
+## Congratulations! :tada:
+
+You've successfully run and modified your React Native App. :partying_face:
+
+### Now what?
+
+- If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps).
+- If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started).
+
+# Troubleshooting
+
+If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page.
+
+# Learn More
+
+To learn more about React Native, take a look at the following resources:
+
+- [React Native Website](https://reactnative.dev) - learn more about React Native.
+- [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment.
+- [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**.
+- [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts.
+- [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native.
diff --git a/third-party/nwaku/examples/mobile/__tests__/App.test.tsx b/third-party/nwaku/examples/mobile/__tests__/App.test.tsx
new file mode 100644
index 0000000..9eac6fb
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/__tests__/App.test.tsx
@@ -0,0 +1,17 @@
+/**
+ * @format
+ */
+
+import 'react-native';
+import React from 'react';
+import App from '../App';
+
+// Note: import explicitly to use the types shipped with jest.
+import {it} from '@jest/globals';
+
+// Note: test renderer must be required after react-native.
+import renderer from 'react-test-renderer';
+
+it('renders correctly', () => {
+ renderer.create();
+});
diff --git a/third-party/nwaku/examples/mobile/android/app/build.gradle b/third-party/nwaku/examples/mobile/android/app/build.gradle
new file mode 100644
index 0000000..46cf8da
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/build.gradle
@@ -0,0 +1,141 @@
+apply plugin: "com.android.application"
+apply plugin: "org.jetbrains.kotlin.android"
+apply plugin: "com.facebook.react"
+
+/**
+ * This is the configuration block to customize your React Native Android app.
+ * By default you don't need to apply any configuration, just uncomment the lines you need.
+ */
+react {
+ /* Folders */
+ // The root of your project, i.e. where "package.json" lives. Default is '..'
+ // root = file("../")
+ // The folder where the react-native NPM package is. Default is ../node_modules/react-native
+ // reactNativeDir = file("../node_modules/react-native")
+ // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
+ // codegenDir = file("../node_modules/@react-native/codegen")
+ // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
+ // cliFile = file("../node_modules/react-native/cli.js")
+
+ /* Variants */
+ // The list of variants to that are debuggable. For those we're going to
+ // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
+ // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
+ // debuggableVariants = ["liteDebug", "prodDebug"]
+
+ /* Bundling */
+ // A list containing the node command and its flags. Default is just 'node'.
+ // nodeExecutableAndArgs = ["node"]
+ //
+ // The command to run when bundling. By default is 'bundle'
+ // bundleCommand = "ram-bundle"
+ //
+ // The path to the CLI configuration file. Default is empty.
+ // bundleConfig = file(../rn-cli.config.js)
+ //
+ // The name of the generated asset file containing your JS bundle
+ // bundleAssetName = "MyApplication.android.bundle"
+ //
+ // The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
+ // entryFile = file("../js/MyApplication.android.js")
+ //
+ // A list of extra flags to pass to the 'bundle' commands.
+ // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
+ // extraPackagerArgs = []
+
+ /* Hermes Commands */
+ // The hermes compiler command to run. By default it is 'hermesc'
+ // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
+ //
+ // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
+ // hermesFlags = ["-O", "-output-source-map"]
+}
+
+/**
+ * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
+ */
+def enableProguardInReleaseBuilds = false
+
+/**
+ * The preferred build flavor of JavaScriptCore (JSC)
+ *
+ * For example, to use the international variant, you can use:
+ * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
+ *
+ * The international variant includes ICU i18n library and necessary data
+ * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
+ * give correct results when using with locales other than en-US. Note that
+ * this variant is about 6MiB larger per architecture than default.
+ */
+def jscFlavor = 'org.webkit:android-jsc:+'
+
+android {
+ ndkVersion rootProject.ext.ndkVersion
+ buildToolsVersion rootProject.ext.buildToolsVersion
+ compileSdk rootProject.ext.compileSdkVersion
+
+ namespace "com.mobile"
+ defaultConfig {
+ applicationId "com.mobile"
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
+ versionCode 1
+ versionName "1.0"
+ ndk {
+ abiFilters "x86_64", "arm64-v8a", "x86" /* "armeabi-v7a", */
+ moduleName "waku_jni"
+ ldLibs "log"
+ cFlags "-std=c99"
+ }
+ }
+ externalNativeBuild {
+ ndkBuild {
+ path file('src/main/jni/Android.mk')
+ }
+ }
+ signingConfigs {
+ debug {
+ storeFile file('debug.keystore')
+ storePassword 'android'
+ keyAlias 'androiddebugkey'
+ keyPassword 'android'
+ }
+ }
+ buildTypes {
+ debug {
+ signingConfig signingConfigs.debug
+ }
+ release {
+ // Caution! In production, you need to generate your own keystore file.
+ // see https://reactnative.dev/docs/signed-apk-android.
+ signingConfig signingConfigs.debug
+ minifyEnabled enableProguardInReleaseBuilds
+ proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
+ }
+ }
+
+ sourceSets {
+ main {
+ jniLibs {
+ srcDirs 'src/main/jniLibs'
+ }
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.aar"])
+
+ // The version of react-native is set by the React Native Gradle Plugin
+ implementation("com.facebook.react:react-android")
+
+ implementation 'com.google.code.gson:gson:2.8.8'
+
+ if (hermesEnabled.toBoolean()) {
+ implementation("com.facebook.react:hermes-android")
+ } else {
+ implementation jscFlavor
+ }
+}
+
+apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
diff --git a/third-party/nwaku/examples/mobile/android/app/debug.keystore b/third-party/nwaku/examples/mobile/android/app/debug.keystore
new file mode 100644
index 0000000..364e105
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/app/debug.keystore differ
diff --git a/third-party/nwaku/examples/mobile/android/app/proguard-rules.pro b/third-party/nwaku/examples/mobile/android/app/proguard-rules.pro
new file mode 100644
index 0000000..11b0257
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/proguard-rules.pro
@@ -0,0 +1,10 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
diff --git a/third-party/nwaku/examples/mobile/android/app/src/debug/AndroidManifest.xml b/third-party/nwaku/examples/mobile/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..fbf78ef
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/AndroidManifest.xml b/third-party/nwaku/examples/mobile/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4122f36
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/MainActivity.kt b/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/MainActivity.kt
new file mode 100644
index 0000000..8b315da
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/MainActivity.kt
@@ -0,0 +1,29 @@
+package com.mobile
+
+import com.facebook.react.ReactActivity
+import com.facebook.react.ReactActivityDelegate
+import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
+import com.facebook.react.defaults.DefaultReactActivityDelegate
+
+class MainActivity : ReactActivity() {
+
+ companion object {
+ init {
+ System.loadLibrary("rln")
+ System.loadLibrary("waku")
+ System.loadLibrary("waku_jni");
+ }
+ }
+ /**
+ * Returns the name of the main component registered from JavaScript. This is used to schedule
+ * rendering of the component.
+ */
+ override fun getMainComponentName(): String = "mobile"
+
+ /**
+ * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
+ * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
+ */
+ override fun createReactActivityDelegate(): ReactActivityDelegate =
+ DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
+}
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/MainApplication.kt b/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/MainApplication.kt
new file mode 100644
index 0000000..79121f1
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/MainApplication.kt
@@ -0,0 +1,43 @@
+package com.mobile
+
+import android.app.Application
+import com.facebook.react.PackageList
+import com.facebook.react.ReactApplication
+import com.facebook.react.ReactHost
+import com.facebook.react.ReactNativeHost
+import com.facebook.react.ReactPackage
+import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
+import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
+import com.facebook.react.defaults.DefaultReactNativeHost
+import com.facebook.soloader.SoLoader
+
+class MainApplication : Application(), ReactApplication {
+
+ override val reactNativeHost: ReactNativeHost =
+ object : DefaultReactNativeHost(this) {
+ override fun getPackages(): List =
+ PackageList(this).packages.apply {
+ // Packages that cannot be autolinked yet can be added manually here, for example:
+ add(MyAppPackage())
+ }
+
+ override fun getJSMainModuleName(): String = "index"
+
+ override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
+
+ override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
+ override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
+ }
+
+ override val reactHost: ReactHost
+ get() = getDefaultReactHost(applicationContext, reactNativeHost)
+
+ override fun onCreate() {
+ super.onCreate()
+ SoLoader.init(this, false)
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
+ // If you opted-in for the New Architecture, we load the native entry point for this app.
+ load()
+ }
+ }
+}
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/ReactNativePackage.kt b/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/ReactNativePackage.kt
new file mode 100644
index 0000000..5e03685
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/ReactNativePackage.kt
@@ -0,0 +1,19 @@
+package com.mobile
+
+import android.view.View
+import com.facebook.react.ReactPackage
+import com.facebook.react.bridge.NativeModule
+import com.facebook.react.bridge.ReactApplicationContext
+import com.facebook.react.uimanager.ReactShadowNode
+import com.facebook.react.uimanager.ViewManager
+
+class MyAppPackage : ReactPackage {
+
+ override fun createViewManagers(
+ reactContext: ReactApplicationContext
+ ): MutableList>> = mutableListOf()
+
+ override fun createNativeModules(
+ reactContext: ReactApplicationContext
+ ): MutableList = listOf(WakuModule(reactContext)).toMutableList()
+}
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/WakuModule.kt b/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/WakuModule.kt
new file mode 100644
index 0000000..4c1c020
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/java/com/mobile/WakuModule.kt
@@ -0,0 +1,247 @@
+package com.mobile
+
+import com.facebook.react.bridge.Arguments
+import com.facebook.react.bridge.Promise
+import com.facebook.react.bridge.ReactApplicationContext
+import com.facebook.react.bridge.ReactContext
+import com.facebook.react.bridge.ReactContextBaseJavaModule
+import com.facebook.react.bridge.ReactMethod
+import com.facebook.react.bridge.ReadableArray
+import com.facebook.react.bridge.ReadableMap
+import com.facebook.react.bridge.ReadableType
+import com.facebook.react.bridge.WritableNativeArray
+import com.facebook.react.modules.core.DeviceEventManagerModule
+import com.google.gson.Gson
+import java.math.BigInteger
+import org.json.JSONArray
+
+class EventCallbackManager {
+ companion object {
+
+ lateinit var reactContext: ReactContext
+
+ @JvmStatic
+ fun execEventCallback(wakuPtr: Long, evt: String) {
+ val params =
+ Arguments.createMap().apply {
+ putString("wakuPtr", wakuPtr.toString())
+ putString("event", evt)
+ }
+
+ reactContext
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
+ .emit("wakuEvent", params)
+ }
+ }
+}
+
+fun convertStringToArray(stringArray: String): WritableNativeArray {
+ val writableArray = WritableNativeArray()
+ val jsonArray = JSONArray(stringArray)
+ for (i in 0 until jsonArray.length()) {
+ writableArray.pushString(jsonArray.getString(i))
+ }
+ return writableArray
+}
+
+fun stringifyReadableMap(map: ReadableMap): String {
+ val gson = Gson()
+ return gson.toJson(readableMapToMap(map))
+}
+
+fun readableMapToMap(readableMap: ReadableMap): Map {
+ val map = mutableMapOf()
+ val iterator = readableMap.keySetIterator()
+ while (iterator.hasNextKey()) {
+ val key = iterator.nextKey()
+ when (readableMap.getType(key)) {
+ ReadableType.Null -> map[key] = null
+ ReadableType.Boolean -> map[key] = readableMap.getBoolean(key)
+ ReadableType.Number -> map[key] = readableMap.getInt(key)
+ ReadableType.String -> map[key] = readableMap.getString(key)
+ ReadableType.Map -> map[key] = readableMapToMap(readableMap.getMap(key)!!)
+ ReadableType.Array -> map[key] = readableArrayToList(readableMap.getArray(key)!!)
+ }
+ }
+ return map
+}
+
+fun readableArrayToList(readableArray: ReadableArray): List {
+ val list = mutableListOf()
+ for (i in 0 until readableArray.size()) {
+ when (readableArray.getType(i)) {
+ ReadableType.Null -> list.add(null)
+ ReadableType.Boolean -> list.add(readableArray.getBoolean(i))
+ ReadableType.Number -> list.add(readableArray.getInt(i))
+ ReadableType.String -> list.add(readableArray.getString(i))
+ ReadableType.Map -> list.add(readableMapToMap(readableArray.getMap(i)))
+ ReadableType.Array -> list.add(readableArrayToList(readableArray.getArray(i)))
+ }
+ }
+ return list
+}
+
+class WakuPtr(val error: Boolean, val errorMessage: String, val ptr: Long)
+
+class WakuResult(val error: Boolean, val message: String)
+
+class WakuModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
+ var reactContext = reactContext
+
+ override fun getName() = "WakuModule"
+
+ external fun wakuSetup()
+ external fun wakuNew(configJson: String): WakuPtr
+ external fun wakuStart(ctx: Long): WakuResult
+ external fun wakuVersion(ctx: Long): WakuResult
+ external fun wakuStop(ctx: Long): WakuResult
+ external fun wakuDestroy(ctx: Long): WakuResult
+ external fun wakuConnect(ctx: Long, peerMultiAddr: String, timeoutMs: Int): WakuResult
+ external fun wakuListenAddresses(ctx: Long): WakuResult
+ external fun wakuRelayPublish(
+ ctx: Long,
+ topic: String,
+ wakuMsg: String,
+ timeoutMs: Int
+ ): WakuResult
+ external fun wakuRelaySubscribe(ctx: Long, topic: String): WakuResult
+ external fun wakuRelayUnsubscribe(ctx: Long, topic: String): WakuResult
+ external fun wakuSetEventCallback(ctx: Long)
+
+ init {
+ EventCallbackManager.reactContext = reactContext
+ }
+
+ @ReactMethod
+ fun setup(promise: Promise) {
+ wakuSetup()
+ promise.resolve(null)
+ }
+
+ @ReactMethod
+ fun new(config: ReadableMap, promise: Promise) {
+ val configStr = stringifyReadableMap(config)
+ val response = wakuNew(configStr)
+ if (response.error) {
+ promise.reject("waku_new", response.errorMessage)
+ } else {
+ // With this we just indicate to waku_ffi that we have registered a
+ // closure, for this wakuPtr. Later once a message is received the
+ // callback manager will receive both the wakuPtr and the message,
+ // and it will use these values to emit a JS event
+ wakuSetEventCallback(response.ptr)
+
+ promise.resolve(BigInteger.valueOf(response.ptr).toString())
+ }
+ }
+
+ @ReactMethod
+ fun start(ctx: String, promise: Promise) {
+ val wakuPtr = BigInteger(ctx).toLong()
+ val response = wakuStart(wakuPtr)
+ if (response.error) {
+ promise.reject("waku_start", response.message)
+ } else {
+ promise.resolve(null)
+ }
+ }
+
+ @ReactMethod
+ fun version(ctx: String, promise: Promise) {
+ val wakuPtr = BigInteger(ctx).toLong()
+ val response = wakuVersion(wakuPtr)
+ if (response.error) {
+ promise.reject("waku_version", response.message)
+ } else {
+ promise.resolve(response.message)
+ }
+ }
+
+ @ReactMethod
+ fun stop(ctx: String, promise: Promise) {
+ val wakuPtr = BigInteger(ctx).toLong()
+ val response = wakuStop(wakuPtr)
+ if (response.error) {
+ promise.reject("waku_stop", response.message)
+ } else {
+ promise.resolve(null)
+ }
+ }
+
+ @ReactMethod
+ fun destroy(ctx: String, promise: Promise) {
+ val wakuPtr = BigInteger(ctx).toLong()
+ val response = wakuDestroy(wakuPtr)
+ if (response.error) {
+ promise.reject("waku_destroy", response.message)
+ } else {
+ promise.resolve(null)
+ }
+ }
+
+ @ReactMethod
+ fun listenAddresses(ctx: String, promise: Promise) {
+ val wakuPtr = BigInteger(ctx).toLong()
+ val response = wakuListenAddresses(wakuPtr)
+ if (response.error) {
+ promise.reject("waku_listen_addresses", response.message)
+ } else {
+ promise.resolve(convertStringToArray(response.message))
+ }
+ }
+
+ @ReactMethod
+ fun connect(ctx: String, peerMultiAddr: String, timeoutMs: Int, promise: Promise) {
+ val wakuPtr = BigInteger(ctx).toLong()
+ val response = wakuConnect(wakuPtr, peerMultiAddr, timeoutMs)
+ if (response.error) {
+ promise.reject("waku_connect", response.message)
+ } else {
+ promise.resolve(null)
+ }
+ }
+
+ @ReactMethod
+ fun relaySubscribe(ctx: String, topic: String, promise: Promise) {
+ val wakuPtr = BigInteger(ctx).toLong()
+ val response = wakuRelaySubscribe(wakuPtr, topic)
+ if (response.error) {
+ promise.reject("waku_relay_subscribe", response.message)
+ } else {
+ promise.resolve(null)
+ }
+ }
+
+ @ReactMethod
+ fun relayUnsubscribe(ctx: String, topic: String, promise: Promise) {
+ val wakuPtr = BigInteger(ctx).toLong()
+ val response = wakuRelayUnsubscribe(wakuPtr, topic)
+ if (response.error) {
+ promise.reject("waku_relay_unsubscribe", response.message)
+ } else {
+ promise.resolve(null)
+ }
+ }
+
+ @ReactMethod
+ fun relayPublish(ctx: String, topic: String, msg: ReadableMap, timeoutMs: Int, promise: Promise) {
+ val wakuPtr = BigInteger(ctx).toLong()
+ val msgStr = stringifyReadableMap(msg)
+ val response = wakuRelayPublish(wakuPtr, topic, msgStr, timeoutMs)
+ if (response.error) {
+ promise.reject("waku_relay_publish", response.message)
+ } else {
+ promise.resolve(null)
+ }
+ }
+
+ @ReactMethod
+ fun addListener(eventName: String) {
+ // No impl required
+ }
+
+ @ReactMethod
+ fun removeListeners(count: Int) {
+ // No impl required
+ }
+}
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/jni/.gitignore b/third-party/nwaku/examples/mobile/android/app/src/main/jni/.gitignore
new file mode 100644
index 0000000..dcb1665
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/jni/.gitignore
@@ -0,0 +1 @@
+libwaku.h
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/jni/Android.mk b/third-party/nwaku/examples/mobile/android/app/src/main/jni/Android.mk
new file mode 100644
index 0000000..f7ec7ff
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/jni/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := waku
+LOCAL_SRC_FILES := ../jniLibs/$(TARGET_ARCH_ABI)/libwaku.so
+
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := waku_ffi.c
+LOCAL_MODULE := waku_jni
+LOCAL_LDLIBS := -llog
+LOCAL_SHARED_LIBRARIES := waku
+
+include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/jni/Application.mk b/third-party/nwaku/examples/mobile/android/app/src/main/jni/Application.mk
new file mode 100644
index 0000000..e619d92
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/jni/Application.mk
@@ -0,0 +1 @@
+APP_ABI := all
\ No newline at end of file
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/jni/waku_ffi.c b/third-party/nwaku/examples/mobile/android/app/src/main/jni/waku_ffi.c
new file mode 100644
index 0000000..477e2da
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/jni/waku_ffi.c
@@ -0,0 +1,325 @@
+#include "libwaku.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define APPNAME "waku-jni"
+#define LOGD(TAG) __android_log_print(ANDROID_LOG_DEBUG , APPNAME,TAG);
+
+// cb_result represents a response received when executing a callback.
+// If `error` is true, `message` will contain the error message description
+// otherwise, it will contain the result of the callback execution
+typedef struct {
+ bool error;
+ char *message;
+} cb_result;
+
+// cb_env is a struct passed as userdata when setting up the event callback.
+// This is so we can pass the pointer back to kotlin to indicate which instance
+// of waku received the message, and also so we can have access to `env` from
+// within the event callback
+typedef struct {
+ jlong wakuPtr;
+ JNIEnv *env;
+} cb_env;
+
+// frees the results associated to the allocation of a cb_result
+void free_cb_result(cb_result *result) {
+ if (result != NULL) {
+ if (result->message != NULL) {
+ free(result->message);
+ result->message = NULL;
+ }
+ free(result);
+ result = NULL;
+ }
+}
+
+// callback executed by libwaku functions. It expects user_data to be a
+// cb_result*.
+void on_response(int ret, const char *msg, size_t len, void *user_data) {
+ if (ret != RET_OK) {
+ char errMsg[300];
+ snprintf(errMsg, 300, "function execution failed. Returned code: %d, %s\n", ret, msg);
+ if (user_data != NULL) {
+ cb_result **data_ref = (cb_result **)user_data;
+ (*data_ref) = malloc(sizeof(cb_result));
+ (*data_ref)->error = true;
+ (*data_ref)->message = malloc(len * sizeof(char) + 1);
+ (*data_ref)->message[0] = '\0';
+ strncat((*data_ref)->message, msg, len);
+ }
+ return;
+ }
+
+ if (user_data == NULL)
+ return;
+
+ if (len == 0) {
+ len = 14;
+ msg = "on_response-ok";
+ }
+
+ cb_result **data_ref = (cb_result **)user_data;
+ (*data_ref) = malloc(sizeof(cb_result));
+ (*data_ref)->error = false;
+ (*data_ref)->message = malloc(len * sizeof(char) + 1);
+ (*data_ref)->message[0] = '\0';
+ strncat((*data_ref)->message, msg, len);
+}
+
+// converts a cb_result into an instance of the kotlin WakuResult class
+jobject to_jni_result(JNIEnv *env, cb_result *result) {
+ jclass myStructClass = (*env)->FindClass(env, "com/mobile/WakuResult");
+ jmethodID constructor = (*env)->GetMethodID(env, myStructClass, "",
+ "(ZLjava/lang/String;)V");
+
+ jboolean error;
+ jstring message;
+ if (result != NULL) {
+ error = result->error;
+ message = (*env)->NewStringUTF(env, result->message);
+ } else {
+ error = false;
+ message = (*env)->NewStringUTF(env, "ok");
+ }
+
+ jobject response =
+ (*env)->NewObject(env, myStructClass, constructor, error, message);
+
+ // Free the intermediate message var
+ (*env)->DeleteLocalRef(env, message);
+
+ return response;
+}
+
+// converts a cb_result into an instance of the kotlin WakuPtr class
+jobject to_jni_ptr(JNIEnv *env, cb_result *result, void *ptr) {
+ jclass myStructClass = (*env)->FindClass(env, "com/mobile/WakuPtr");
+ jmethodID constructor = (*env)->GetMethodID(env, myStructClass, "",
+ "(ZLjava/lang/String;J)V");
+
+ jboolean error;
+ jstring message;
+ jlong wakuPtr;
+ if (result != NULL) {
+ error = result->error;
+ message = (*env)->NewStringUTF(env, result->message);
+ wakuPtr = -1;
+ } else {
+ error = false;
+ message = (*env)->NewStringUTF(env, "ok");
+ wakuPtr = (jlong)ptr;
+ }
+
+ jobject response = (*env)->NewObject(env, myStructClass, constructor, error,
+ message, wakuPtr);
+
+ // Free the intermediate message var
+ (*env)->DeleteLocalRef(env, message);
+
+ return response;
+}
+
+// libwaku functions
+// ============================================================================
+
+// JVM, required for executing JNI functions in a third party thread.
+JavaVM *jvm;
+static jobject jClassLoader;
+static jmethodID jLoadClass;
+
+JNIEnv *getEnv() {
+ JNIEnv *env;
+ int status = (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_6);
+ if (status < 0) {
+ status = (*jvm)->AttachCurrentThread(jvm, &env, NULL);
+ assert(status == JNI_OK && "could not obtain env");
+ }
+ return env;
+}
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) {
+ jvm = pjvm; // cache the JavaVM pointer
+ JNIEnv *env = getEnv();
+
+ jclass jLibraryClass =
+ (*env)->FindClass(env, "com/mobile/EventCallbackManager");
+ jclass jClassRef = (*env)->GetObjectClass(env, jLibraryClass);
+ jclass jClassLoaderClass = (*env)->FindClass(env, "java/lang/ClassLoader");
+ jmethodID getClassLoader = (*env)->GetMethodID(
+ env, jClassRef, "getClassLoader", "()Ljava/lang/ClassLoader;");
+
+ jobject jClassLoaderLocal =
+ (*env)->CallObjectMethod(env, jLibraryClass, getClassLoader);
+ jLoadClass = (*env)->GetMethodID(env, jClassLoaderClass, "loadClass",
+ "(Ljava/lang/String;)Ljava/lang/Class;");
+ jClassLoader = (*env)->NewGlobalRef(env, jClassLoaderLocal);
+
+ (*env)->DeleteLocalRef(env, jClassLoaderLocal);
+ (*env)->DeleteLocalRef(env, jClassLoaderClass);
+ (*env)->DeleteLocalRef(env, jClassRef);
+ (*env)->DeleteLocalRef(env, jLibraryClass);
+
+ return JNI_VERSION_1_6;
+}
+
+jclass loadClass(JNIEnv *env, const char *className) {
+ jstring jName = (*env)->NewStringUTF(env, className);
+ jclass jClass = (*env)->CallObjectMethod(env, jClassLoader, jLoadClass, jName);
+ assert((*env)->ExceptionCheck(env) == JNI_FALSE && "class could not be loaded");
+ (*env)->DeleteLocalRef(env, jName);
+ return jClass;
+}
+
+void Java_com_mobile_WakuModule_wakuSetup(JNIEnv *env, jobject thiz) {
+ LOGD("log example for debugging purposes...")
+}
+
+jobject Java_com_mobile_WakuModule_wakuNew(JNIEnv *env, jobject thiz,
+ jstring configJson) {
+ const char *config = (*env)->GetStringUTFChars(env, configJson, 0);
+ cb_result *result = NULL;
+ void *wakuPtr = waku_new(config, on_response, (void *)&result);
+ jobject response = to_jni_ptr(env, result, wakuPtr);
+ (*env)->ReleaseStringUTFChars(env, configJson, config);
+ free_cb_result(result);
+ return response;
+}
+
+jobject Java_com_mobile_WakuModule_wakuStart(JNIEnv *env, jobject thiz,
+ jlong wakuPtr) {
+ cb_result *result = NULL;
+ waku_start((void *)wakuPtr, on_response, &result);
+ jobject response = to_jni_result(env, result);
+ free_cb_result(result);
+ return response;
+}
+
+jobject Java_com_mobile_WakuModule_wakuVersion(JNIEnv *env, jobject thiz,
+ jlong wakuPtr) {
+ cb_result *result = NULL;
+ waku_version((void *)wakuPtr, on_response, (void *)&result);
+ jobject response = to_jni_result(env, result);
+ free_cb_result(result);
+ return response;
+}
+
+jobject Java_com_mobile_WakuModule_wakuStop(JNIEnv *env, jobject thiz,
+ jlong wakuPtr) {
+ cb_result *result = NULL;
+ waku_stop((void *)wakuPtr, on_response, &result);
+ jobject response = to_jni_result(env, result);
+ free_cb_result(result);
+ return response;
+}
+
+jobject Java_com_mobile_WakuModule_wakuDestroy(JNIEnv *env, jobject thiz,
+ jlong wakuPtr) {
+ cb_result *result = NULL;
+ waku_destroy((void *)wakuPtr, on_response, &result);
+ jobject response = to_jni_result(env, result);
+ free_cb_result(result);
+ return response;
+}
+
+jobject Java_com_mobile_WakuModule_wakuConnect(JNIEnv *env, jobject thiz,
+ jlong wakuPtr,
+ jstring peerMultiAddr,
+ jint timeoutMs) {
+ cb_result *result = NULL;
+ const char *peer = (*env)->GetStringUTFChars(env, peerMultiAddr, 0);
+ waku_connect((void *)wakuPtr, peer, timeoutMs, on_response, &result);
+ jobject response = to_jni_result(env, result);
+ free_cb_result(result);
+ (*env)->ReleaseStringUTFChars(env, peerMultiAddr, peer);
+ return response;
+}
+
+jobject Java_com_mobile_WakuModule_wakuListenAddresses(JNIEnv *env,
+ jobject thiz,
+ jlong wakuPtr) {
+ cb_result *result = NULL;
+ waku_listen_addresses((void *)wakuPtr, on_response, (void *)&result);
+ jobject response = to_jni_result(env, result);
+ free_cb_result(result);
+ return response;
+}
+
+jobject Java_com_mobile_WakuModule_wakuRelayPublish(JNIEnv *env, jobject thiz,
+ jlong wakuPtr,
+ jstring pubsubTopic,
+ jstring jsonWakuMessage,
+ jint timeoutMs) {
+ cb_result *result = NULL;
+ const char *topic = (*env)->GetStringUTFChars(env, pubsubTopic, 0);
+ const char *msg = (*env)->GetStringUTFChars(env, jsonWakuMessage, 0);
+ waku_relay_publish((void *)wakuPtr, topic, msg, timeoutMs, on_response,
+ (void *)&result);
+ jobject response = to_jni_result(env, result);
+ free_cb_result(result);
+ (*env)->ReleaseStringUTFChars(env, pubsubTopic, topic);
+ (*env)->ReleaseStringUTFChars(env, jsonWakuMessage, msg);
+ return response;
+}
+
+jobject Java_com_mobile_WakuModule_wakuRelaySubscribe(JNIEnv *env, jobject thiz,
+ jlong wakuPtr,
+ jstring pubsubTopic) {
+ cb_result *result = NULL;
+ const char *topic = (*env)->GetStringUTFChars(env, pubsubTopic, 0);
+ waku_relay_subscribe((void *)wakuPtr, topic, on_response, (void *)&result);
+ jobject response = to_jni_result(env, result);
+ free_cb_result(result);
+ (*env)->ReleaseStringUTFChars(env, pubsubTopic, topic);
+ return response;
+}
+
+jobject Java_com_mobile_WakuModule_wakuRelayUnsubscribe(JNIEnv *env,
+ jobject thiz,
+ jlong wakuPtr,
+ jstring pubsubTopic) {
+ cb_result *result = NULL;
+ const char *topic = (*env)->GetStringUTFChars(env, pubsubTopic, 0);
+ waku_relay_unsubscribe((void *)wakuPtr, topic, on_response, (void *)&result);
+ jobject response = to_jni_result(env, result);
+ free_cb_result(result);
+ (*env)->ReleaseStringUTFChars(env, pubsubTopic, topic);
+ return response;
+}
+
+void wk_callback(int callerRet, const char *msg, size_t len, void *userData) {
+ cb_env *c = (cb_env *)userData;
+
+ // TODO: might be too much overhead to attach/detach per call?
+ JNIEnv *env = c->env;
+ JNIEnv *attachedEnv = NULL;
+ assert((*jvm)->AttachCurrentThread(jvm, &attachedEnv, NULL) == JNI_OK && "could not attach to current thread");
+
+ jclass clazz = loadClass(attachedEnv, "com/mobile/EventCallbackManager");
+
+ jmethodID methodID =
+ (*attachedEnv)
+ ->GetStaticMethodID(attachedEnv, clazz, "execEventCallback", "(JLjava/lang/String;)V");
+
+ jstring message = (*attachedEnv)->NewStringUTF(attachedEnv, msg);
+ (*attachedEnv)->CallStaticVoidMethod(attachedEnv, clazz, methodID, c->wakuPtr, message);
+
+ (*attachedEnv)->DeleteLocalRef(attachedEnv, clazz);
+
+ (*attachedEnv)->DeleteLocalRef(attachedEnv, message);
+
+ (*jvm)->DetachCurrentThread(jvm);
+}
+
+void Java_com_mobile_WakuModule_wakuSetEventCallback(JNIEnv *env, jobject thiz,
+ jlong wakuPtr) {
+ cb_env *c = (cb_env *)malloc(sizeof(cb_env));
+ c->wakuPtr = wakuPtr;
+ c->env = env;
+ waku_set_event_callback((void *)wakuPtr, wk_callback, (void *)c);
+}
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/jniLibs/arm64-v8a/.gitkeep b/third-party/nwaku/examples/mobile/android/app/src/main/jniLibs/arm64-v8a/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/jniLibs/armeabi-v7a/.gitkeep b/third-party/nwaku/examples/mobile/android/app/src/main/jniLibs/armeabi-v7a/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/jniLibs/x86/.gitkeep b/third-party/nwaku/examples/mobile/android/app/src/main/jniLibs/x86/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/jniLibs/x86_64/.gitkeep b/third-party/nwaku/examples/mobile/android/app/src/main/jniLibs/x86_64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/drawable/rn_edit_text_material.xml b/third-party/nwaku/examples/mobile/android/app/src/main/res/drawable/rn_edit_text_material.xml
new file mode 100644
index 0000000..5c25e72
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/res/drawable/rn_edit_text_material.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a2f5908
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..1b52399
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..ff10afd
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..115a4c7
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..dcd3cd8
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..459ca60
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..8ca12fe
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..8e19b41
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b824ebd
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4c19a13
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/values/strings.xml b/third-party/nwaku/examples/mobile/android/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..377157c
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ mobile
+
diff --git a/third-party/nwaku/examples/mobile/android/app/src/main/res/values/styles.xml b/third-party/nwaku/examples/mobile/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..7ba83a2
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/third-party/nwaku/examples/mobile/android/build.gradle b/third-party/nwaku/examples/mobile/android/build.gradle
new file mode 100644
index 0000000..03443cd
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/build.gradle
@@ -0,0 +1,21 @@
+buildscript {
+ ext {
+ buildToolsVersion = "34.0.0"
+ minSdkVersion = 30
+ compileSdkVersion = 34
+ targetSdkVersion = 34
+ ndkVersion = "26.1.10909125"
+ kotlinVersion = "1.9.22"
+ }
+ repositories {
+ google()
+ mavenCentral()
+ }
+ dependencies {
+ classpath("com.android.tools.build:gradle")
+ classpath("com.facebook.react:react-native-gradle-plugin")
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
+ }
+}
+
+apply plugin: "com.facebook.react.rootproject"
diff --git a/third-party/nwaku/examples/mobile/android/gradle.properties b/third-party/nwaku/examples/mobile/android/gradle.properties
new file mode 100644
index 0000000..a46a5b9
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/gradle.properties
@@ -0,0 +1,41 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
+org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+
+# Use this property to specify which architecture you want to build.
+# You can also override it from the CLI using
+# ./gradlew -PreactNativeArchitectures=x86_64
+reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
+
+# Use this property to enable support to the new architecture.
+# This will allow you to use TurboModules and the Fabric render in
+# your application. You should enable this flag either if you want
+# to write custom TurboModules/Fabric components OR use libraries that
+# are providing them.
+newArchEnabled=false
+
+# Use this property to enable or disable the Hermes JS engine.
+# If set to false, you will be using JSC instead.
+hermesEnabled=true
diff --git a/third-party/nwaku/examples/mobile/android/gradle/wrapper/gradle-wrapper.jar b/third-party/nwaku/examples/mobile/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..7f93135
Binary files /dev/null and b/third-party/nwaku/examples/mobile/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/third-party/nwaku/examples/mobile/android/gradle/wrapper/gradle-wrapper.properties b/third-party/nwaku/examples/mobile/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..2ea3535
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/third-party/nwaku/examples/mobile/android/gradlew b/third-party/nwaku/examples/mobile/android/gradlew
new file mode 100755
index 0000000..1aa94a4
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/gradlew
@@ -0,0 +1,249 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/third-party/nwaku/examples/mobile/android/gradlew.bat b/third-party/nwaku/examples/mobile/android/gradlew.bat
new file mode 100644
index 0000000..7101f8e
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/gradlew.bat
@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/third-party/nwaku/examples/mobile/android/settings.gradle b/third-party/nwaku/examples/mobile/android/settings.gradle
new file mode 100644
index 0000000..fa70819
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/android/settings.gradle
@@ -0,0 +1,4 @@
+rootProject.name = 'mobile'
+apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
+include ':app'
+includeBuild('../node_modules/@react-native/gradle-plugin')
diff --git a/third-party/nwaku/examples/mobile/app.json b/third-party/nwaku/examples/mobile/app.json
new file mode 100644
index 0000000..1cd68dc
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/app.json
@@ -0,0 +1,4 @@
+{
+ "name": "mobile",
+ "displayName": "mobile"
+}
diff --git a/third-party/nwaku/examples/mobile/babel.config.js b/third-party/nwaku/examples/mobile/babel.config.js
new file mode 100644
index 0000000..f7b3da3
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ presets: ['module:@react-native/babel-preset'],
+};
diff --git a/third-party/nwaku/examples/mobile/build-nwaku.js b/third-party/nwaku/examples/mobile/build-nwaku.js
new file mode 100644
index 0000000..53f3dba
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/build-nwaku.js
@@ -0,0 +1,64 @@
+const fs = require('fs-extra');
+const {spawn} = require('child_process');
+
+// Parse command line arguments
+const args = process.argv.slice(2);
+const forceFlagIndex = args.indexOf('--force');
+
+const nwakuRootFolder = '../../';
+const libwakuHeaderSrc = 'library/libwaku.h';
+
+// Android --------------------------------------------------------------------------------------
+
+const androidArchitectures = ['arm64-v8a', 'x86', 'x86_64']; // 'armeabi-v7a'
+const androidSrcFolder = 'build/android';
+const androidDstFolder = 'android/app/src/main/jniLibs';
+const androidFilesToCheck = ['libwaku.so', 'librln.so'];
+const androidLibDst = 'android/app/src/main/jni/libwaku.h';
+
+const androidDstFiles = [androidLibDst];
+androidArchitectures.forEach(architecture => {
+ androidFilesToCheck.forEach(file => {
+ androidDstFiles.push(`${androidDstFolder}/${architecture}/${file}`);
+ });
+});
+
+// Check if all files exist
+const filesExist = androidDstFiles.every(file => fs.existsSync(file));
+if (!filesExist || forceFlagIndex !== -1) {
+ console.log('Running make to generate all architecture libraries...');
+ const makeCommand = 'make';
+ const makeProcess = spawn(makeCommand, ['libwaku-android'], {cwd: '../../'});
+
+ makeProcess.stdout.on('data', data => process.stdout.write(data));
+ makeProcess.stderr.on('data', data => process.stdout.write(data));
+ makeProcess.on('close', code => {
+ if (code == 0) {
+ console.log('Copying generated libraries...');
+ androidArchitectures.forEach(architecture => {
+ androidFilesToCheck.forEach(file => {
+ androidDstFiles.push(`${androidDstFolder}/${architecture}/${file}`);
+ fs.copyFile(
+ `${nwakuRootFolder}/${androidSrcFolder}/${architecture}/${file}`,
+ `${androidDstFolder}/${architecture}/${file}`,
+ err => {
+ if (err) throw err;
+ },
+ );
+ });
+ });
+ console.log('Copying header...');
+ fs.copyFile(
+ `${nwakuRootFolder}/${libwakuHeaderSrc}`,
+ androidLibDst,
+ err => {
+ if (err) throw err;
+ },
+ );
+ } else {
+ console.error(`make exited with ${code}`);
+ }
+ });
+} else {
+ console.log('All files exist. Skipping make.');
+}
diff --git a/third-party/nwaku/examples/mobile/index.js b/third-party/nwaku/examples/mobile/index.js
new file mode 100644
index 0000000..a850d03
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/index.js
@@ -0,0 +1,9 @@
+/**
+ * @format
+ */
+
+import {AppRegistry} from 'react-native';
+import App from './App';
+import {name as appName} from './app.json';
+
+AppRegistry.registerComponent(appName, () => App);
diff --git a/third-party/nwaku/examples/mobile/ios/.xcode.env b/third-party/nwaku/examples/mobile/ios/.xcode.env
new file mode 100644
index 0000000..3d5782c
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/.xcode.env
@@ -0,0 +1,11 @@
+# This `.xcode.env` file is versioned and is used to source the environment
+# used when running script phases inside Xcode.
+# To customize your local environment, you can create an `.xcode.env.local`
+# file that is not versioned.
+
+# NODE_BINARY variable contains the PATH to the node executable.
+#
+# Customize the NODE_BINARY variable here.
+# For example, to use nvm with brew, add the following line
+# . "$(brew --prefix nvm)/nvm.sh" --no-use
+export NODE_BINARY=$(command -v node)
diff --git a/third-party/nwaku/examples/mobile/ios/Podfile b/third-party/nwaku/examples/mobile/ios/Podfile
new file mode 100644
index 0000000..3a46190
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/Podfile
@@ -0,0 +1,40 @@
+# Resolve react_native_pods.rb with node to allow for hoisting
+require Pod::Executable.execute_command('node', ['-p',
+ 'require.resolve(
+ "react-native/scripts/react_native_pods.rb",
+ {paths: [process.argv[1]]},
+ )', __dir__]).strip
+
+platform :ios, min_ios_version_supported
+prepare_react_native_project!
+
+linkage = ENV['USE_FRAMEWORKS']
+if linkage != nil
+ Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
+ use_frameworks! :linkage => linkage.to_sym
+end
+
+target 'mobile' do
+ config = use_native_modules!
+
+ use_react_native!(
+ :path => config[:reactNativePath],
+ # An absolute path to your application root.
+ :app_path => "#{Pod::Config.instance.installation_root}/.."
+ )
+
+ target 'mobileTests' do
+ inherit! :complete
+ # Pods for testing
+ end
+
+ post_install do |installer|
+ # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
+ react_native_post_install(
+ installer,
+ config[:reactNativePath],
+ :mac_catalyst_enabled => false,
+ # :ccache_enabled => true
+ )
+ end
+end
diff --git a/third-party/nwaku/examples/mobile/ios/mobile.xcodeproj/project.pbxproj b/third-party/nwaku/examples/mobile/ios/mobile.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..1c67104
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobile.xcodeproj/project.pbxproj
@@ -0,0 +1,688 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 00E356F31AD99517003FC87E /* mobileTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* mobileTests.m */; };
+ 0C80B921A6F3F58F76C31292 /* libPods-mobile.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-mobile.a */; };
+ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
+ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
+ 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
+ 7699B88040F8A987B510C191 /* libPods-mobile-mobileTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-mobile-mobileTests.a */; };
+ 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
+ remoteInfo = mobile;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 00E356EE1AD99517003FC87E /* mobileTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = mobileTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 00E356F21AD99517003FC87E /* mobileTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = mobileTests.m; sourceTree = ""; };
+ 13B07F961A680F5B00A75B9A /* mobile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = mobile.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = mobile/AppDelegate.h; sourceTree = ""; };
+ 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = mobile/AppDelegate.mm; sourceTree = ""; };
+ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = mobile/Images.xcassets; sourceTree = ""; };
+ 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = mobile/Info.plist; sourceTree = ""; };
+ 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = mobile/main.m; sourceTree = ""; };
+ 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = mobile/PrivacyInfo.xcprivacy; sourceTree = ""; };
+ 19F6CBCC0A4E27FBF8BF4A61 /* libPods-mobile-mobileTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-mobile-mobileTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3B4392A12AC88292D35C810B /* Pods-mobile.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mobile.debug.xcconfig"; path = "Target Support Files/Pods-mobile/Pods-mobile.debug.xcconfig"; sourceTree = ""; };
+ 5709B34CF0A7D63546082F79 /* Pods-mobile.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mobile.release.xcconfig"; path = "Target Support Files/Pods-mobile/Pods-mobile.release.xcconfig"; sourceTree = ""; };
+ 5B7EB9410499542E8C5724F5 /* Pods-mobile-mobileTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mobile-mobileTests.debug.xcconfig"; path = "Target Support Files/Pods-mobile-mobileTests/Pods-mobile-mobileTests.debug.xcconfig"; sourceTree = ""; };
+ 5DCACB8F33CDC322A6C60F78 /* libPods-mobile.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-mobile.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = mobile/LaunchScreen.storyboard; sourceTree = ""; };
+ 89C6BE57DB24E9ADA2F236DE /* Pods-mobile-mobileTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mobile-mobileTests.release.xcconfig"; path = "Target Support Files/Pods-mobile-mobileTests/Pods-mobile-mobileTests.release.xcconfig"; sourceTree = ""; };
+ ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 00E356EB1AD99517003FC87E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7699B88040F8A987B510C191 /* libPods-mobile-mobileTests.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 0C80B921A6F3F58F76C31292 /* libPods-mobile.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 00E356EF1AD99517003FC87E /* mobileTests */ = {
+ isa = PBXGroup;
+ children = (
+ 00E356F21AD99517003FC87E /* mobileTests.m */,
+ 00E356F01AD99517003FC87E /* Supporting Files */,
+ );
+ path = mobileTests;
+ sourceTree = "";
+ };
+ 00E356F01AD99517003FC87E /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 00E356F11AD99517003FC87E /* Info.plist */,
+ );
+ name = "Supporting Files";
+ sourceTree = "";
+ };
+ 13B07FAE1A68108700A75B9A /* mobile */ = {
+ isa = PBXGroup;
+ children = (
+ 13B07FAF1A68108700A75B9A /* AppDelegate.h */,
+ 13B07FB01A68108700A75B9A /* AppDelegate.mm */,
+ 13B07FB51A68108700A75B9A /* Images.xcassets */,
+ 13B07FB61A68108700A75B9A /* Info.plist */,
+ 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
+ 13B07FB71A68108700A75B9A /* main.m */,
+ 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
+ );
+ name = mobile;
+ sourceTree = "";
+ };
+ 2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
+ 5DCACB8F33CDC322A6C60F78 /* libPods-mobile.a */,
+ 19F6CBCC0A4E27FBF8BF4A61 /* libPods-mobile-mobileTests.a */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Libraries;
+ sourceTree = "";
+ };
+ 83CBB9F61A601CBA00E9B192 = {
+ isa = PBXGroup;
+ children = (
+ 13B07FAE1A68108700A75B9A /* mobile */,
+ 832341AE1AAA6A7D00B99B32 /* Libraries */,
+ 00E356EF1AD99517003FC87E /* mobileTests */,
+ 83CBBA001A601CBA00E9B192 /* Products */,
+ 2D16E6871FA4F8E400B85C8A /* Frameworks */,
+ BBD78D7AC51CEA395F1C20DB /* Pods */,
+ );
+ indentWidth = 2;
+ sourceTree = "";
+ tabWidth = 2;
+ usesTabs = 0;
+ };
+ 83CBBA001A601CBA00E9B192 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 13B07F961A680F5B00A75B9A /* mobile.app */,
+ 00E356EE1AD99517003FC87E /* mobileTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ BBD78D7AC51CEA395F1C20DB /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 3B4392A12AC88292D35C810B /* Pods-mobile.debug.xcconfig */,
+ 5709B34CF0A7D63546082F79 /* Pods-mobile.release.xcconfig */,
+ 5B7EB9410499542E8C5724F5 /* Pods-mobile-mobileTests.debug.xcconfig */,
+ 89C6BE57DB24E9ADA2F236DE /* Pods-mobile-mobileTests.release.xcconfig */,
+ );
+ path = Pods;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 00E356ED1AD99517003FC87E /* mobileTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "mobileTests" */;
+ buildPhases = (
+ A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */,
+ 00E356EA1AD99517003FC87E /* Sources */,
+ 00E356EB1AD99517003FC87E /* Frameworks */,
+ 00E356EC1AD99517003FC87E /* Resources */,
+ C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */,
+ F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 00E356F51AD99517003FC87E /* PBXTargetDependency */,
+ );
+ name = mobileTests;
+ productName = mobileTests;
+ productReference = 00E356EE1AD99517003FC87E /* mobileTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 13B07F861A680F5B00A75B9A /* mobile */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "mobile" */;
+ buildPhases = (
+ C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */,
+ 13B07F871A680F5B00A75B9A /* Sources */,
+ 13B07F8C1A680F5B00A75B9A /* Frameworks */,
+ 13B07F8E1A680F5B00A75B9A /* Resources */,
+ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
+ 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */,
+ E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = mobile;
+ productName = mobile;
+ productReference = 13B07F961A680F5B00A75B9A /* mobile.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 83CBB9F71A601CBA00E9B192 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1210;
+ TargetAttributes = {
+ 00E356ED1AD99517003FC87E = {
+ CreatedOnToolsVersion = 6.2;
+ TestTargetID = 13B07F861A680F5B00A75B9A;
+ };
+ 13B07F861A680F5B00A75B9A = {
+ LastSwiftMigration = 1120;
+ };
+ };
+ };
+ buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "mobile" */;
+ compatibilityVersion = "Xcode 12.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 83CBB9F61A601CBA00E9B192;
+ productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 13B07F861A680F5B00A75B9A /* mobile */,
+ 00E356ED1AD99517003FC87E /* mobileTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 00E356EC1AD99517003FC87E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 13B07F8E1A680F5B00A75B9A /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
+ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "$(SRCROOT)/.xcode.env.local",
+ "$(SRCROOT)/.xcode.env",
+ );
+ name = "Bundle React Native code and images";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
+ };
+ 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-mobile/Pods-mobile-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-mobile/Pods-mobile-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-mobile/Pods-mobile-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-mobile-mobileTests-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-mobile-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-mobile-mobileTests/Pods-mobile-mobileTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-mobile-mobileTests/Pods-mobile-mobileTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-mobile-mobileTests/Pods-mobile-mobileTests-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-mobile/Pods-mobile-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-mobile/Pods-mobile-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-mobile/Pods-mobile-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-mobile-mobileTests/Pods-mobile-mobileTests-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-mobile-mobileTests/Pods-mobile-mobileTests-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-mobile-mobileTests/Pods-mobile-mobileTests-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 00E356EA1AD99517003FC87E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 00E356F31AD99517003FC87E /* mobileTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 13B07F871A680F5B00A75B9A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
+ 13B07FC11A68108700A75B9A /* main.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 13B07F861A680F5B00A75B9A /* mobile */;
+ targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 00E356F61AD99517003FC87E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-mobile-mobileTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = mobileTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.4;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "-lc++",
+ "$(inherited)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mobile.app/mobile";
+ };
+ name = Debug;
+ };
+ 00E356F71AD99517003FC87E /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-mobile-mobileTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ COPY_PHASE_STRIP = NO;
+ INFOPLIST_FILE = mobileTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.4;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "-lc++",
+ "$(inherited)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mobile.app/mobile";
+ };
+ name = Release;
+ };
+ 13B07F941A680F5B00A75B9A /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-mobile.debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = mobile/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-ObjC",
+ "-lc++",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_NAME = mobile;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 13B07F951A680F5B00A75B9A /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-mobile.release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ INFOPLIST_FILE = mobile/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-ObjC",
+ "-lc++",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_NAME = mobile;
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+ 83CBBA201A601CBA00E9B192 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++20";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.4;
+ LD_RUNPATH_SEARCH_PATHS = (
+ /usr/lib/swift,
+ "$(inherited)",
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "\"$(SDKROOT)/usr/lib/swift\"",
+ "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
+ "\"$(inherited)\"",
+ );
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "-DFOLLY_NO_CONFIG",
+ "-DFOLLY_MOBILE=1",
+ "-DFOLLY_USE_LIBCPP=1",
+ "-DFOLLY_CFG_NO_COROUTINES=1",
+ "-DFOLLY_HAVE_CLOCK_GETTIME=1",
+ );
+ SDKROOT = iphoneos;
+ };
+ name = Debug;
+ };
+ 83CBBA211A601CBA00E9B192 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++20";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.4;
+ LD_RUNPATH_SEARCH_PATHS = (
+ /usr/lib/swift,
+ "$(inherited)",
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "\"$(SDKROOT)/usr/lib/swift\"",
+ "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
+ "\"$(inherited)\"",
+ );
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "-DFOLLY_NO_CONFIG",
+ "-DFOLLY_MOBILE=1",
+ "-DFOLLY_USE_LIBCPP=1",
+ "-DFOLLY_CFG_NO_COROUTINES=1",
+ "-DFOLLY_HAVE_CLOCK_GETTIME=1",
+ );
+ SDKROOT = iphoneos;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "mobileTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00E356F61AD99517003FC87E /* Debug */,
+ 00E356F71AD99517003FC87E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "mobile" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 13B07F941A680F5B00A75B9A /* Debug */,
+ 13B07F951A680F5B00A75B9A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "mobile" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 83CBBA201A601CBA00E9B192 /* Debug */,
+ 83CBBA211A601CBA00E9B192 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
+}
diff --git a/third-party/nwaku/examples/mobile/ios/mobile.xcodeproj/xcshareddata/xcschemes/mobile.xcscheme b/third-party/nwaku/examples/mobile/ios/mobile.xcodeproj/xcshareddata/xcschemes/mobile.xcscheme
new file mode 100644
index 0000000..85dd574
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobile.xcodeproj/xcshareddata/xcschemes/mobile.xcscheme
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third-party/nwaku/examples/mobile/ios/mobile/AppDelegate.h b/third-party/nwaku/examples/mobile/ios/mobile/AppDelegate.h
new file mode 100644
index 0000000..5d28082
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobile/AppDelegate.h
@@ -0,0 +1,6 @@
+#import
+#import
+
+@interface AppDelegate : RCTAppDelegate
+
+@end
diff --git a/third-party/nwaku/examples/mobile/ios/mobile/AppDelegate.mm b/third-party/nwaku/examples/mobile/ios/mobile/AppDelegate.mm
new file mode 100644
index 0000000..9555b55
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobile/AppDelegate.mm
@@ -0,0 +1,31 @@
+#import "AppDelegate.h"
+
+#import
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+ self.moduleName = @"mobile";
+ // You can add your custom initial props in the dictionary below.
+ // They will be passed down to the ViewController used by React Native.
+ self.initialProps = @{};
+
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
+}
+
+- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
+{
+ return [self bundleURL];
+}
+
+- (NSURL *)bundleURL
+{
+#if DEBUG
+ return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
+#else
+ return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
+#endif
+}
+
+@end
diff --git a/third-party/nwaku/examples/mobile/ios/mobile/Images.xcassets/AppIcon.appiconset/Contents.json b/third-party/nwaku/examples/mobile/ios/mobile/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..8121323
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobile/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,53 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/third-party/nwaku/examples/mobile/ios/mobile/Images.xcassets/Contents.json b/third-party/nwaku/examples/mobile/ios/mobile/Images.xcassets/Contents.json
new file mode 100644
index 0000000..2d92bd5
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobile/Images.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/third-party/nwaku/examples/mobile/ios/mobile/Info.plist b/third-party/nwaku/examples/mobile/ios/mobile/Info.plist
new file mode 100644
index 0000000..9ef592f
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobile/Info.plist
@@ -0,0 +1,52 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ mobile
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(MARKETING_VERSION)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ LSRequiresIPhoneOS
+
+ NSAppTransportSecurity
+
+
+ NSAllowsArbitraryLoads
+
+ NSAllowsLocalNetworking
+
+
+ NSLocationWhenInUseUsageDescription
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/third-party/nwaku/examples/mobile/ios/mobile/LaunchScreen.storyboard b/third-party/nwaku/examples/mobile/ios/mobile/LaunchScreen.storyboard
new file mode 100644
index 0000000..61d1bfd
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobile/LaunchScreen.storyboard
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third-party/nwaku/examples/mobile/ios/mobile/PrivacyInfo.xcprivacy b/third-party/nwaku/examples/mobile/ios/mobile/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000..ef1896e
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobile/PrivacyInfo.xcprivacy
@@ -0,0 +1,38 @@
+
+
+
+
+ NSPrivacyCollectedDataTypes
+
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryFileTimestamp
+ NSPrivacyAccessedAPITypeReasons
+
+ C617.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryUserDefaults
+ NSPrivacyAccessedAPITypeReasons
+
+ CA92.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategorySystemBootTime
+ NSPrivacyAccessedAPITypeReasons
+
+ 35F9.1
+
+
+
+ NSPrivacyTracking
+
+
+
diff --git a/third-party/nwaku/examples/mobile/ios/mobile/main.m b/third-party/nwaku/examples/mobile/ios/mobile/main.m
new file mode 100644
index 0000000..d645c72
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobile/main.m
@@ -0,0 +1,10 @@
+#import
+
+#import "AppDelegate.h"
+
+int main(int argc, char *argv[])
+{
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/third-party/nwaku/examples/mobile/ios/mobileTests/Info.plist b/third-party/nwaku/examples/mobile/ios/mobileTests/Info.plist
new file mode 100644
index 0000000..ba72822
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobileTests/Info.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ BNDL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+
+
diff --git a/third-party/nwaku/examples/mobile/ios/mobileTests/mobileTests.m b/third-party/nwaku/examples/mobile/ios/mobileTests/mobileTests.m
new file mode 100644
index 0000000..be4fe75
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/ios/mobileTests/mobileTests.m
@@ -0,0 +1,66 @@
+#import
+#import
+
+#import
+#import
+
+#define TIMEOUT_SECONDS 600
+#define TEXT_TO_LOOK_FOR @"Welcome to React"
+
+@interface mobileTests : XCTestCase
+
+@end
+
+@implementation mobileTests
+
+- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
+{
+ if (test(view)) {
+ return YES;
+ }
+ for (UIView *subview in [view subviews]) {
+ if ([self findSubviewInView:subview matching:test]) {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (void)testRendersWelcomeScreen
+{
+ UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
+ NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
+ BOOL foundElement = NO;
+
+ __block NSString *redboxError = nil;
+#ifdef DEBUG
+ RCTSetLogFunction(
+ ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
+ if (level >= RCTLogLevelError) {
+ redboxError = message;
+ }
+ });
+#endif
+
+ while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
+ [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
+ [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
+
+ foundElement = [self findSubviewInView:vc.view
+ matching:^BOOL(UIView *view) {
+ if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
+ return YES;
+ }
+ return NO;
+ }];
+ }
+
+#ifdef DEBUG
+ RCTSetLogFunction(RCTDefaultLogFunction);
+#endif
+
+ XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
+ XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
+}
+
+@end
diff --git a/third-party/nwaku/examples/mobile/jest.config.js b/third-party/nwaku/examples/mobile/jest.config.js
new file mode 100644
index 0000000..8eb675e
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/jest.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ preset: 'react-native',
+};
diff --git a/third-party/nwaku/examples/mobile/metro.config.js b/third-party/nwaku/examples/mobile/metro.config.js
new file mode 100644
index 0000000..9d41685
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/metro.config.js
@@ -0,0 +1,11 @@
+const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
+
+/**
+ * Metro configuration
+ * https://reactnative.dev/docs/metro
+ *
+ * @type {import('metro-config').MetroConfig}
+ */
+const config = {};
+
+module.exports = mergeConfig(getDefaultConfig(__dirname), config);
diff --git a/third-party/nwaku/examples/mobile/package.json b/third-party/nwaku/examples/mobile/package.json
new file mode 100644
index 0000000..c4e5f0c
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "mobile",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "prestart": "node build-nwaku.js",
+ "android": "react-native run-android",
+ "ios": "react-native run-ios",
+ "lint": "eslint .",
+ "start": "react-native start",
+ "test": "jest",
+ "force-generate-mobile-libs": "node build-nwaku.js --force"
+ },
+ "dependencies": {
+ "react": "18.2.0",
+ "react-native": "0.74.0"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.20.0",
+ "@babel/preset-env": "^7.20.0",
+ "@babel/runtime": "^7.20.0",
+ "@react-native/babel-preset": "0.74.81",
+ "@react-native/eslint-config": "0.74.81",
+ "@react-native/metro-config": "0.74.81",
+ "@react-native/typescript-config": "0.74.81",
+ "@types/react": "^18.2.6",
+ "@types/react-test-renderer": "^18.0.0",
+ "babel-jest": "^29.6.3",
+ "eslint": "^8.19.0",
+ "jest": "^29.6.3",
+ "prettier": "2.8.8",
+ "react-test-renderer": "18.2.0",
+ "typescript": "5.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+}
diff --git a/third-party/nwaku/examples/mobile/tsconfig.json b/third-party/nwaku/examples/mobile/tsconfig.json
new file mode 100644
index 0000000..304ab4e
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/tsconfig.json
@@ -0,0 +1,3 @@
+{
+ "extends": "@react-native/typescript-config/tsconfig.json"
+}
diff --git a/third-party/nwaku/examples/mobile/yarn.lock b/third-party/nwaku/examples/mobile/yarn.lock
new file mode 100644
index 0000000..1902232
--- /dev/null
+++ b/third-party/nwaku/examples/mobile/yarn.lock
@@ -0,0 +1,6747 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@aashutoshrathi/word-wrap@^1.2.3":
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
+ integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
+
+"@ampproject/remapping@^2.2.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
+ integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5":
+ version "7.23.5"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244"
+ integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==
+ dependencies:
+ "@babel/highlight" "^7.23.4"
+ chalk "^2.4.2"
+
+"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.5":
+ version "7.23.5"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98"
+ integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==
+
+"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.0", "@babel/core@^7.23.9":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.0.tgz#56cbda6b185ae9d9bed369816a8f4423c5f2ff1b"
+ integrity sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==
+ dependencies:
+ "@ampproject/remapping" "^2.2.0"
+ "@babel/code-frame" "^7.23.5"
+ "@babel/generator" "^7.23.6"
+ "@babel/helper-compilation-targets" "^7.23.6"
+ "@babel/helper-module-transforms" "^7.23.3"
+ "@babel/helpers" "^7.24.0"
+ "@babel/parser" "^7.24.0"
+ "@babel/template" "^7.24.0"
+ "@babel/traverse" "^7.24.0"
+ "@babel/types" "^7.24.0"
+ convert-source-map "^2.0.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.2.3"
+ semver "^6.3.1"
+
+"@babel/eslint-parser@^7.20.0":
+ version "7.23.10"
+ resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.23.10.tgz#2d4164842d6db798873b40e0c4238827084667a2"
+ integrity sha512-3wSYDPZVnhseRnxRJH6ZVTNknBz76AEnyC+AYYhasjP3Yy23qz0ERR7Fcd2SHmYuSFJ2kY9gaaDd3vyqU09eSw==
+ dependencies:
+ "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1"
+ eslint-visitor-keys "^2.1.0"
+ semver "^6.3.1"
+
+"@babel/generator@^7.20.0", "@babel/generator@^7.23.6", "@babel/generator@^7.7.2":
+ version "7.23.6"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e"
+ integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==
+ dependencies:
+ "@babel/types" "^7.23.6"
+ "@jridgewell/gen-mapping" "^0.3.2"
+ "@jridgewell/trace-mapping" "^0.3.17"
+ jsesc "^2.5.1"
+
+"@babel/helper-annotate-as-pure@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882"
+ integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15":
+ version "7.22.15"
+ resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956"
+ integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==
+ dependencies:
+ "@babel/types" "^7.22.15"
+
+"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6":
+ version "7.23.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991"
+ integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==
+ dependencies:
+ "@babel/compat-data" "^7.23.5"
+ "@babel/helper-validator-option" "^7.23.5"
+ browserslist "^4.22.2"
+ lru-cache "^5.1.1"
+ semver "^6.3.1"
+
+"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.15", "@babel/helper-create-class-features-plugin@^7.23.6":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.0.tgz#fc7554141bdbfa2d17f7b4b80153b9b090e5d158"
+ integrity sha512-QAH+vfvts51BCsNZ2PhY6HAggnlS6omLLFTsIpeqZk/MmJ6cW7tgz5yRv0fMJThcr6FmbMrENh1RgrWPTYA76g==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-function-name" "^7.23.0"
+ "@babel/helper-member-expression-to-functions" "^7.23.0"
+ "@babel/helper-optimise-call-expression" "^7.22.5"
+ "@babel/helper-replace-supers" "^7.22.20"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5"
+ "@babel/helper-split-export-declaration" "^7.22.6"
+ semver "^6.3.1"
+
+"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5":
+ version "7.22.15"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1"
+ integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ regexpu-core "^5.3.1"
+ semver "^6.3.1"
+
+"@babel/helper-define-polyfill-provider@^0.5.0":
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz#465805b7361f461e86c680f1de21eaf88c25901b"
+ integrity sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==
+ dependencies:
+ "@babel/helper-compilation-targets" "^7.22.6"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ debug "^4.1.1"
+ lodash.debounce "^4.0.8"
+ resolve "^1.14.2"
+
+"@babel/helper-define-polyfill-provider@^0.6.1":
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.1.tgz#fadc63f0c2ff3c8d02ed905dcea747c5b0fb74fd"
+ integrity sha512-o7SDgTJuvx5vLKD6SFvkydkSMBvahDKGiNJzG22IZYXhiqoe9efY7zocICBgzHV4IRg5wdgl2nEL/tulKIEIbA==
+ dependencies:
+ "@babel/helper-compilation-targets" "^7.22.6"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ debug "^4.1.1"
+ lodash.debounce "^4.0.8"
+ resolve "^1.14.2"
+
+"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20":
+ version "7.22.20"
+ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
+ integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
+
+"@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.23.0":
+ version "7.23.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
+ integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
+ dependencies:
+ "@babel/template" "^7.22.15"
+ "@babel/types" "^7.23.0"
+
+"@babel/helper-hoist-variables@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb"
+ integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
+"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.23.0":
+ version "7.23.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366"
+ integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==
+ dependencies:
+ "@babel/types" "^7.23.0"
+
+"@babel/helper-module-imports@^7.22.15":
+ version "7.22.15"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0"
+ integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==
+ dependencies:
+ "@babel/types" "^7.22.15"
+
+"@babel/helper-module-transforms@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1"
+ integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-module-imports" "^7.22.15"
+ "@babel/helper-simple-access" "^7.22.5"
+ "@babel/helper-split-export-declaration" "^7.22.6"
+ "@babel/helper-validator-identifier" "^7.22.20"
+
+"@babel/helper-optimise-call-expression@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e"
+ integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a"
+ integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==
+
+"@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.22.20":
+ version "7.22.20"
+ resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0"
+ integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-wrap-function" "^7.22.20"
+
+"@babel/helper-replace-supers@^7.22.20":
+ version "7.22.20"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793"
+ integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-member-expression-to-functions" "^7.22.15"
+ "@babel/helper-optimise-call-expression" "^7.22.5"
+
+"@babel/helper-simple-access@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de"
+ integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
+"@babel/helper-skip-transparent-expression-wrappers@^7.20.0", "@babel/helper-skip-transparent-expression-wrappers@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847"
+ integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
+"@babel/helper-split-export-declaration@^7.22.6":
+ version "7.22.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
+ integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
+"@babel/helper-string-parser@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83"
+ integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==
+
+"@babel/helper-validator-identifier@^7.22.20":
+ version "7.22.20"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
+ integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
+
+"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5":
+ version "7.23.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307"
+ integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==
+
+"@babel/helper-wrap-function@^7.22.20":
+ version "7.22.20"
+ resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569"
+ integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==
+ dependencies:
+ "@babel/helper-function-name" "^7.22.5"
+ "@babel/template" "^7.22.15"
+ "@babel/types" "^7.22.19"
+
+"@babel/helpers@^7.24.0":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.0.tgz#a3dd462b41769c95db8091e49cfe019389a9409b"
+ integrity sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==
+ dependencies:
+ "@babel/template" "^7.24.0"
+ "@babel/traverse" "^7.24.0"
+ "@babel/types" "^7.24.0"
+
+"@babel/highlight@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b"
+ integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.22.20"
+ chalk "^2.4.2"
+ js-tokens "^4.0.0"
+
+"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.0.tgz#26a3d1ff49031c53a97d03b604375f028746a9ac"
+ integrity sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==
+
+"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a"
+ integrity sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz#f6652bb16b94f8f9c20c50941e16e9756898dc5d"
+ integrity sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5"
+ "@babel/plugin-transform-optional-chaining" "^7.23.3"
+
+"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.7":
+ version "7.23.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz#516462a95d10a9618f197d39ad291a9b47ae1d7b"
+ integrity sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-proposal-async-generator-functions@^7.0.0":
+ version "7.20.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326"
+ integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.18.9"
+ "@babel/helper-plugin-utils" "^7.20.2"
+ "@babel/helper-remap-async-to-generator" "^7.18.9"
+ "@babel/plugin-syntax-async-generators" "^7.8.4"
+
+"@babel/plugin-proposal-class-properties@^7.13.0", "@babel/plugin-proposal-class-properties@^7.18.0":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3"
+ integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.18.6"
+ "@babel/helper-plugin-utils" "^7.18.6"
+
+"@babel/plugin-proposal-export-default-from@^7.0.0":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.23.3.tgz#6f511a676c540ccc8d17a8553dbba9230b0ddac0"
+ integrity sha512-Q23MpLZfSGZL1kU7fWqV262q65svLSCIP5kZ/JCW/rKTCm/FrLjpvEd2kfUYMVeHh4QhV/xzyoRAHWrAZJrE3Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-export-default-from" "^7.23.3"
+
+"@babel/plugin-proposal-logical-assignment-operators@^7.18.0":
+ version "7.20.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83"
+ integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.20.2"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.0":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1"
+ integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.18.6"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+
+"@babel/plugin-proposal-numeric-separator@^7.0.0":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75"
+ integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.18.6"
+ "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+
+"@babel/plugin-proposal-object-rest-spread@^7.20.0":
+ version "7.20.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a"
+ integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==
+ dependencies:
+ "@babel/compat-data" "^7.20.5"
+ "@babel/helper-compilation-targets" "^7.20.7"
+ "@babel/helper-plugin-utils" "^7.20.2"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+ "@babel/plugin-transform-parameters" "^7.20.7"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.0.0":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb"
+ integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.18.6"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+
+"@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.20.0":
+ version "7.21.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea"
+ integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.20.2"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+
+"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2":
+ version "7.21.0-placeholder-for-preset-env.2"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703"
+ integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==
+
+"@babel/plugin-syntax-async-generators@^7.8.4":
+ version "7.8.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
+ integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-bigint@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea"
+ integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10"
+ integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.12.13"
+
+"@babel/plugin-syntax-class-static-block@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406"
+ integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
+ integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.23.3.tgz#7e6d4bf595d5724230200fb2b7401d4734b15335"
+ integrity sha512-KeENO5ck1IeZ/l2lFZNy+mpobV3D2Zy5C1YFnWm+YuY5mQiAWc4yAp13dqgguwsBsFVLh4LPCEqCa5qW13N+hw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-syntax-export-namespace-from@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a"
+ integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-flow@^7.12.1", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz#084564e0f3cc21ea6c70c44cff984a1c0509729a"
+ integrity sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-syntax-import-assertions@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz#9c05a7f592982aff1a2768260ad84bcd3f0c77fc"
+ integrity sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-syntax-import-attributes@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz#992aee922cf04512461d7dae3ff6951b90a2dc06"
+ integrity sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51"
+ integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-json-strings@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
+ integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-jsx@^7.23.3", "@babel/plugin-syntax-jsx@^7.7.2":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473"
+ integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
+ integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.0.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
+ integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
+ integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-object-rest-spread@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
+ integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
+ integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.0.0", "@babel/plugin-syntax-optional-chaining@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+ integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-private-property-in-object@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad"
+ integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c"
+ integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-typescript@^7.23.3", "@babel/plugin-syntax-typescript@^7.7.2":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f"
+ integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-syntax-unicode-sets-regex@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357"
+ integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.18.6"
+ "@babel/helper-plugin-utils" "^7.18.6"
+
+"@babel/plugin-transform-arrow-functions@^7.0.0", "@babel/plugin-transform-arrow-functions@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b"
+ integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-async-generator-functions@^7.23.9":
+ version "7.23.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz#9adaeb66fc9634a586c5df139c6240d41ed801ce"
+ integrity sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-remap-async-to-generator" "^7.22.20"
+ "@babel/plugin-syntax-async-generators" "^7.8.4"
+
+"@babel/plugin-transform-async-to-generator@^7.20.0", "@babel/plugin-transform-async-to-generator@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz#d1f513c7a8a506d43f47df2bf25f9254b0b051fa"
+ integrity sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==
+ dependencies:
+ "@babel/helper-module-imports" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-remap-async-to-generator" "^7.22.20"
+
+"@babel/plugin-transform-block-scoped-functions@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77"
+ integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-block-scoping@^7.0.0", "@babel/plugin-transform-block-scoping@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz#b2d38589531c6c80fbe25e6b58e763622d2d3cf5"
+ integrity sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-class-properties@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz#35c377db11ca92a785a718b6aa4e3ed1eb65dc48"
+ integrity sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-class-static-block@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz#2a202c8787a8964dd11dfcedf994d36bfc844ab5"
+ integrity sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-class-static-block" "^7.14.5"
+
+"@babel/plugin-transform-classes@^7.0.0", "@babel/plugin-transform-classes@^7.23.8":
+ version "7.23.8"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz#d08ae096c240347badd68cdf1b6d1624a6435d92"
+ integrity sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ "@babel/helper-compilation-targets" "^7.23.6"
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-function-name" "^7.23.0"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-replace-supers" "^7.22.20"
+ "@babel/helper-split-export-declaration" "^7.22.6"
+ globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.0.0", "@babel/plugin-transform-computed-properties@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474"
+ integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/template" "^7.22.15"
+
+"@babel/plugin-transform-destructuring@^7.20.0", "@babel/plugin-transform-destructuring@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311"
+ integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-dotall-regex@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz#3f7af6054882ede89c378d0cf889b854a993da50"
+ integrity sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-duplicate-keys@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz#664706ca0a5dfe8d066537f99032fc1dc8b720ce"
+ integrity sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-dynamic-import@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz#c7629e7254011ac3630d47d7f34ddd40ca535143"
+ integrity sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.3"
+
+"@babel/plugin-transform-exponentiation-operator@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz#ea0d978f6b9232ba4722f3dbecdd18f450babd18"
+ integrity sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==
+ dependencies:
+ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-export-namespace-from@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz#084c7b25e9a5c8271e987a08cf85807b80283191"
+ integrity sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+
+"@babel/plugin-transform-flow-strip-types@^7.20.0", "@babel/plugin-transform-flow-strip-types@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.23.3.tgz#cfa7ca159cc3306fab526fc67091556b51af26ff"
+ integrity sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-flow" "^7.23.3"
+
+"@babel/plugin-transform-for-of@^7.23.6":
+ version "7.23.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz#81c37e24171b37b370ba6aaffa7ac86bcb46f94e"
+ integrity sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5"
+
+"@babel/plugin-transform-function-name@^7.0.0", "@babel/plugin-transform-function-name@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc"
+ integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==
+ dependencies:
+ "@babel/helper-compilation-targets" "^7.22.15"
+ "@babel/helper-function-name" "^7.23.0"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-json-strings@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz#a871d9b6bd171976efad2e43e694c961ffa3714d"
+ integrity sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-json-strings" "^7.8.3"
+
+"@babel/plugin-transform-literals@^7.0.0", "@babel/plugin-transform-literals@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4"
+ integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-logical-assignment-operators@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz#e599f82c51d55fac725f62ce55d3a0886279ecb5"
+ integrity sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+
+"@babel/plugin-transform-member-expression-literals@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc"
+ integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-modules-amd@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz#e19b55436a1416829df0a1afc495deedfae17f7d"
+ integrity sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.23.3"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4"
+ integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.23.3"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-simple-access" "^7.22.5"
+
+"@babel/plugin-transform-modules-systemjs@^7.23.9":
+ version "7.23.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz#105d3ed46e4a21d257f83a2f9e2ee4203ceda6be"
+ integrity sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==
+ dependencies:
+ "@babel/helper-hoist-variables" "^7.22.5"
+ "@babel/helper-module-transforms" "^7.23.3"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-validator-identifier" "^7.22.20"
+
+"@babel/plugin-transform-modules-umd@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz#5d4395fccd071dfefe6585a4411aa7d6b7d769e9"
+ integrity sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.23.3"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.0.0", "@babel/plugin-transform-named-capturing-groups-regex@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f"
+ integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.22.5"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-new-target@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz#5491bb78ed6ac87e990957cea367eab781c4d980"
+ integrity sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-nullish-coalescing-operator@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz#45556aad123fc6e52189ea749e33ce090637346e"
+ integrity sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+
+"@babel/plugin-transform-numeric-separator@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz#03d08e3691e405804ecdd19dd278a40cca531f29"
+ integrity sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+
+"@babel/plugin-transform-object-rest-spread@^7.24.0":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.0.tgz#7b836ad0088fdded2420ce96d4e1d3ed78b71df1"
+ integrity sha512-y/yKMm7buHpFFXfxVFS4Vk1ToRJDilIa6fKRioB9Vjichv58TDGXTvqV0dN7plobAmTW5eSEGXDngE+Mm+uO+w==
+ dependencies:
+ "@babel/compat-data" "^7.23.5"
+ "@babel/helper-compilation-targets" "^7.23.6"
+ "@babel/helper-plugin-utils" "^7.24.0"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+ "@babel/plugin-transform-parameters" "^7.23.3"
+
+"@babel/plugin-transform-object-super@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd"
+ integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-replace-supers" "^7.22.20"
+
+"@babel/plugin-transform-optional-catch-binding@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz#318066de6dacce7d92fa244ae475aa8d91778017"
+ integrity sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+
+"@babel/plugin-transform-optional-chaining@^7.23.3", "@babel/plugin-transform-optional-chaining@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz#6acf61203bdfc4de9d4e52e64490aeb3e52bd017"
+ integrity sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+
+"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af"
+ integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-private-methods@^7.22.5", "@babel/plugin-transform-private-methods@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz#b2d7a3c97e278bfe59137a978d53b2c2e038c0e4"
+ integrity sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-private-property-in-object@^7.22.11", "@babel/plugin-transform-private-property-in-object@^7.23.4":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz#3ec711d05d6608fd173d9b8de39872d8dbf68bf5"
+ integrity sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ "@babel/helper-create-class-features-plugin" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
+
+"@babel/plugin-transform-property-literals@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875"
+ integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-react-display-name@^7.0.0":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz#70529f034dd1e561045ad3c8152a267f0d7b6200"
+ integrity sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-react-jsx-self@^7.0.0":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz#ed3e7dadde046cce761a8e3cf003a13d1a7972d9"
+ integrity sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-react-jsx-source@^7.0.0":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz#03527006bdc8775247a78643c51d4e715fe39a3e"
+ integrity sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-react-jsx@^7.0.0":
+ version "7.23.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312"
+ integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ "@babel/helper-module-imports" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-jsx" "^7.23.3"
+ "@babel/types" "^7.23.4"
+
+"@babel/plugin-transform-regenerator@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz#141afd4a2057298602069fce7f2dc5173e6c561c"
+ integrity sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ regenerator-transform "^0.15.2"
+
+"@babel/plugin-transform-reserved-words@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz#4130dcee12bd3dd5705c587947eb715da12efac8"
+ integrity sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-runtime@^7.0.0":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.0.tgz#e308fe27d08b74027d42547081eefaf4f2ffbcc9"
+ integrity sha512-zc0GA5IitLKJrSfXlXmp8KDqLrnGECK7YRfQBmEKg1NmBOQ7e+KuclBEKJgzifQeUYLdNiAw4B4bjyvzWVLiSA==
+ dependencies:
+ "@babel/helper-module-imports" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.24.0"
+ babel-plugin-polyfill-corejs2 "^0.4.8"
+ babel-plugin-polyfill-corejs3 "^0.9.0"
+ babel-plugin-polyfill-regenerator "^0.5.5"
+ semver "^6.3.1"
+
+"@babel/plugin-transform-shorthand-properties@^7.0.0", "@babel/plugin-transform-shorthand-properties@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210"
+ integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-spread@^7.0.0", "@babel/plugin-transform-spread@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c"
+ integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5"
+
+"@babel/plugin-transform-sticky-regex@^7.0.0", "@babel/plugin-transform-sticky-regex@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz#dec45588ab4a723cb579c609b294a3d1bd22ff04"
+ integrity sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-template-literals@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07"
+ integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-typeof-symbol@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz#9dfab97acc87495c0c449014eb9c547d8966bca4"
+ integrity sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-typescript@^7.23.3", "@babel/plugin-transform-typescript@^7.5.0":
+ version "7.23.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz#aa36a94e5da8d94339ae3a4e22d40ed287feb34c"
+ integrity sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ "@babel/helper-create-class-features-plugin" "^7.23.6"
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/plugin-syntax-typescript" "^7.23.3"
+
+"@babel/plugin-transform-unicode-escapes@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz#1f66d16cab01fab98d784867d24f70c1ca65b925"
+ integrity sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-unicode-property-regex@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz#19e234129e5ffa7205010feec0d94c251083d7ad"
+ integrity sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-unicode-regex@^7.0.0", "@babel/plugin-transform-unicode-regex@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz#26897708d8f42654ca4ce1b73e96140fbad879dc"
+ integrity sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-transform-unicode-sets-regex@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz#4fb6f0a719c2c5859d11f6b55a050cc987f3799e"
+ integrity sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.22.15"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/preset-env@^7.20.0":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.24.0.tgz#11536a7f4b977294f0bdfad780f01a8ac8e183fc"
+ integrity sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==
+ dependencies:
+ "@babel/compat-data" "^7.23.5"
+ "@babel/helper-compilation-targets" "^7.23.6"
+ "@babel/helper-plugin-utils" "^7.24.0"
+ "@babel/helper-validator-option" "^7.23.5"
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3"
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3"
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.7"
+ "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2"
+ "@babel/plugin-syntax-async-generators" "^7.8.4"
+ "@babel/plugin-syntax-class-properties" "^7.12.13"
+ "@babel/plugin-syntax-class-static-block" "^7.14.5"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.3"
+ "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+ "@babel/plugin-syntax-import-assertions" "^7.23.3"
+ "@babel/plugin-syntax-import-attributes" "^7.23.3"
+ "@babel/plugin-syntax-import-meta" "^7.10.4"
+ "@babel/plugin-syntax-json-strings" "^7.8.3"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+ "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+ "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
+ "@babel/plugin-syntax-top-level-await" "^7.14.5"
+ "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6"
+ "@babel/plugin-transform-arrow-functions" "^7.23.3"
+ "@babel/plugin-transform-async-generator-functions" "^7.23.9"
+ "@babel/plugin-transform-async-to-generator" "^7.23.3"
+ "@babel/plugin-transform-block-scoped-functions" "^7.23.3"
+ "@babel/plugin-transform-block-scoping" "^7.23.4"
+ "@babel/plugin-transform-class-properties" "^7.23.3"
+ "@babel/plugin-transform-class-static-block" "^7.23.4"
+ "@babel/plugin-transform-classes" "^7.23.8"
+ "@babel/plugin-transform-computed-properties" "^7.23.3"
+ "@babel/plugin-transform-destructuring" "^7.23.3"
+ "@babel/plugin-transform-dotall-regex" "^7.23.3"
+ "@babel/plugin-transform-duplicate-keys" "^7.23.3"
+ "@babel/plugin-transform-dynamic-import" "^7.23.4"
+ "@babel/plugin-transform-exponentiation-operator" "^7.23.3"
+ "@babel/plugin-transform-export-namespace-from" "^7.23.4"
+ "@babel/plugin-transform-for-of" "^7.23.6"
+ "@babel/plugin-transform-function-name" "^7.23.3"
+ "@babel/plugin-transform-json-strings" "^7.23.4"
+ "@babel/plugin-transform-literals" "^7.23.3"
+ "@babel/plugin-transform-logical-assignment-operators" "^7.23.4"
+ "@babel/plugin-transform-member-expression-literals" "^7.23.3"
+ "@babel/plugin-transform-modules-amd" "^7.23.3"
+ "@babel/plugin-transform-modules-commonjs" "^7.23.3"
+ "@babel/plugin-transform-modules-systemjs" "^7.23.9"
+ "@babel/plugin-transform-modules-umd" "^7.23.3"
+ "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5"
+ "@babel/plugin-transform-new-target" "^7.23.3"
+ "@babel/plugin-transform-nullish-coalescing-operator" "^7.23.4"
+ "@babel/plugin-transform-numeric-separator" "^7.23.4"
+ "@babel/plugin-transform-object-rest-spread" "^7.24.0"
+ "@babel/plugin-transform-object-super" "^7.23.3"
+ "@babel/plugin-transform-optional-catch-binding" "^7.23.4"
+ "@babel/plugin-transform-optional-chaining" "^7.23.4"
+ "@babel/plugin-transform-parameters" "^7.23.3"
+ "@babel/plugin-transform-private-methods" "^7.23.3"
+ "@babel/plugin-transform-private-property-in-object" "^7.23.4"
+ "@babel/plugin-transform-property-literals" "^7.23.3"
+ "@babel/plugin-transform-regenerator" "^7.23.3"
+ "@babel/plugin-transform-reserved-words" "^7.23.3"
+ "@babel/plugin-transform-shorthand-properties" "^7.23.3"
+ "@babel/plugin-transform-spread" "^7.23.3"
+ "@babel/plugin-transform-sticky-regex" "^7.23.3"
+ "@babel/plugin-transform-template-literals" "^7.23.3"
+ "@babel/plugin-transform-typeof-symbol" "^7.23.3"
+ "@babel/plugin-transform-unicode-escapes" "^7.23.3"
+ "@babel/plugin-transform-unicode-property-regex" "^7.23.3"
+ "@babel/plugin-transform-unicode-regex" "^7.23.3"
+ "@babel/plugin-transform-unicode-sets-regex" "^7.23.3"
+ "@babel/preset-modules" "0.1.6-no-external-plugins"
+ babel-plugin-polyfill-corejs2 "^0.4.8"
+ babel-plugin-polyfill-corejs3 "^0.9.0"
+ babel-plugin-polyfill-regenerator "^0.5.5"
+ core-js-compat "^3.31.0"
+ semver "^6.3.1"
+
+"@babel/preset-flow@^7.13.13":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.24.0.tgz#0de60271b0a439b415501c5b28f685fbcb080e1c"
+ integrity sha512-cum/nSi82cDaSJ21I4PgLTVlj0OXovFk6GRguJYe/IKg6y6JHLTbJhybtX4k35WT9wdeJfEVjycTixMhBHd0Dg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.24.0"
+ "@babel/helper-validator-option" "^7.23.5"
+ "@babel/plugin-transform-flow-strip-types" "^7.23.3"
+
+"@babel/preset-modules@0.1.6-no-external-plugins":
+ version "0.1.6-no-external-plugins"
+ resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a"
+ integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/types" "^7.4.4"
+ esutils "^2.0.2"
+
+"@babel/preset-typescript@^7.13.0":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz#14534b34ed5b6d435aa05f1ae1c5e7adcc01d913"
+ integrity sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-validator-option" "^7.22.15"
+ "@babel/plugin-syntax-jsx" "^7.23.3"
+ "@babel/plugin-transform-modules-commonjs" "^7.23.3"
+ "@babel/plugin-transform-typescript" "^7.23.3"
+
+"@babel/register@^7.13.16":
+ version "7.23.7"
+ resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.23.7.tgz#485a5e7951939d21304cae4af1719fdb887bc038"
+ integrity sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ==
+ dependencies:
+ clone-deep "^4.0.1"
+ find-cache-dir "^2.0.0"
+ make-dir "^2.1.0"
+ pirates "^4.0.6"
+ source-map-support "^0.5.16"
+
+"@babel/regjsgen@^0.8.0":
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
+ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
+
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.20.0", "@babel/runtime@^7.8.4":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.0.tgz#584c450063ffda59697021430cb47101b085951e"
+ integrity sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==
+ dependencies:
+ regenerator-runtime "^0.14.0"
+
+"@babel/template@^7.0.0", "@babel/template@^7.22.15", "@babel/template@^7.24.0", "@babel/template@^7.3.3":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50"
+ integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==
+ dependencies:
+ "@babel/code-frame" "^7.23.5"
+ "@babel/parser" "^7.24.0"
+ "@babel/types" "^7.24.0"
+
+"@babel/traverse@^7.20.0", "@babel/traverse@^7.24.0":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.0.tgz#4a408fbf364ff73135c714a2ab46a5eab2831b1e"
+ integrity sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==
+ dependencies:
+ "@babel/code-frame" "^7.23.5"
+ "@babel/generator" "^7.23.6"
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-function-name" "^7.23.0"
+ "@babel/helper-hoist-variables" "^7.22.5"
+ "@babel/helper-split-export-declaration" "^7.22.6"
+ "@babel/parser" "^7.24.0"
+ "@babel/types" "^7.24.0"
+ debug "^4.3.1"
+ globals "^11.1.0"
+
+"@babel/types@^7.0.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.23.6", "@babel/types@^7.24.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
+ version "7.24.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf"
+ integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==
+ dependencies:
+ "@babel/helper-string-parser" "^7.23.4"
+ "@babel/helper-validator-identifier" "^7.22.20"
+ to-fast-properties "^2.0.0"
+
+"@bcoe/v8-coverage@^0.2.3":
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
+ integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
+
+"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
+ integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
+ dependencies:
+ eslint-visitor-keys "^3.3.0"
+
+"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1":
+ version "4.10.0"
+ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63"
+ integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==
+
+"@eslint/eslintrc@^2.1.4":
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad"
+ integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.3.2"
+ espree "^9.6.0"
+ globals "^13.19.0"
+ ignore "^5.2.0"
+ import-fresh "^3.2.1"
+ js-yaml "^4.1.0"
+ minimatch "^3.1.2"
+ strip-json-comments "^3.1.1"
+
+"@eslint/js@8.57.0":
+ version "8.57.0"
+ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
+ integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
+
+"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
+ version "9.3.0"
+ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
+ integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==
+
+"@hapi/topo@^5.1.0":
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
+ integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
+ dependencies:
+ "@hapi/hoek" "^9.0.0"
+
+"@humanwhocodes/config-array@^0.11.14":
+ version "0.11.14"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
+ integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==
+ dependencies:
+ "@humanwhocodes/object-schema" "^2.0.2"
+ debug "^4.3.1"
+ minimatch "^3.0.5"
+
+"@humanwhocodes/module-importer@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+ integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
+"@humanwhocodes/object-schema@^2.0.2":
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
+ integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
+
+"@isaacs/ttlcache@^1.4.1":
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz#21fb23db34e9b6220c6ba023a0118a2dd3461ea2"
+ integrity sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==
+
+"@istanbuljs/load-nyc-config@^1.0.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
+ integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==
+ dependencies:
+ camelcase "^5.3.1"
+ find-up "^4.1.0"
+ get-package-type "^0.1.0"
+ js-yaml "^3.13.1"
+ resolve-from "^5.0.0"
+
+"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3":
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
+ integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
+
+"@jest/console@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc"
+ integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
+ slash "^3.0.0"
+
+"@jest/core@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f"
+ integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==
+ dependencies:
+ "@jest/console" "^29.7.0"
+ "@jest/reporters" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ ansi-escapes "^4.2.1"
+ chalk "^4.0.0"
+ ci-info "^3.2.0"
+ exit "^0.1.2"
+ graceful-fs "^4.2.9"
+ jest-changed-files "^29.7.0"
+ jest-config "^29.7.0"
+ jest-haste-map "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-regex-util "^29.6.3"
+ jest-resolve "^29.7.0"
+ jest-resolve-dependencies "^29.7.0"
+ jest-runner "^29.7.0"
+ jest-runtime "^29.7.0"
+ jest-snapshot "^29.7.0"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ jest-watcher "^29.7.0"
+ micromatch "^4.0.4"
+ pretty-format "^29.7.0"
+ slash "^3.0.0"
+ strip-ansi "^6.0.0"
+
+"@jest/create-cache-key-function@^29.6.3":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz#793be38148fab78e65f40ae30c36785f4ad859f0"
+ integrity sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==
+ dependencies:
+ "@jest/types" "^29.6.3"
+
+"@jest/environment@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7"
+ integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==
+ dependencies:
+ "@jest/fake-timers" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ jest-mock "^29.7.0"
+
+"@jest/expect-utils@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6"
+ integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==
+ dependencies:
+ jest-get-type "^29.6.3"
+
+"@jest/expect@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2"
+ integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==
+ dependencies:
+ expect "^29.7.0"
+ jest-snapshot "^29.7.0"
+
+"@jest/fake-timers@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565"
+ integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ "@sinonjs/fake-timers" "^10.0.2"
+ "@types/node" "*"
+ jest-message-util "^29.7.0"
+ jest-mock "^29.7.0"
+ jest-util "^29.7.0"
+
+"@jest/globals@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d"
+ integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==
+ dependencies:
+ "@jest/environment" "^29.7.0"
+ "@jest/expect" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ jest-mock "^29.7.0"
+
+"@jest/reporters@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7"
+ integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==
+ dependencies:
+ "@bcoe/v8-coverage" "^0.2.3"
+ "@jest/console" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@jridgewell/trace-mapping" "^0.3.18"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ collect-v8-coverage "^1.0.0"
+ exit "^0.1.2"
+ glob "^7.1.3"
+ graceful-fs "^4.2.9"
+ istanbul-lib-coverage "^3.0.0"
+ istanbul-lib-instrument "^6.0.0"
+ istanbul-lib-report "^3.0.0"
+ istanbul-lib-source-maps "^4.0.0"
+ istanbul-reports "^3.1.3"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
+ jest-worker "^29.7.0"
+ slash "^3.0.0"
+ string-length "^4.0.1"
+ strip-ansi "^6.0.0"
+ v8-to-istanbul "^9.0.1"
+
+"@jest/schemas@^29.6.3":
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03"
+ integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==
+ dependencies:
+ "@sinclair/typebox" "^0.27.8"
+
+"@jest/source-map@^29.6.3":
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4"
+ integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==
+ dependencies:
+ "@jridgewell/trace-mapping" "^0.3.18"
+ callsites "^3.0.0"
+ graceful-fs "^4.2.9"
+
+"@jest/test-result@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c"
+ integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==
+ dependencies:
+ "@jest/console" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ collect-v8-coverage "^1.0.0"
+
+"@jest/test-sequencer@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce"
+ integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==
+ dependencies:
+ "@jest/test-result" "^29.7.0"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
+ slash "^3.0.0"
+
+"@jest/transform@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c"
+ integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==
+ dependencies:
+ "@babel/core" "^7.11.6"
+ "@jest/types" "^29.6.3"
+ "@jridgewell/trace-mapping" "^0.3.18"
+ babel-plugin-istanbul "^6.1.1"
+ chalk "^4.0.0"
+ convert-source-map "^2.0.0"
+ fast-json-stable-stringify "^2.1.0"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
+ jest-regex-util "^29.6.3"
+ jest-util "^29.7.0"
+ micromatch "^4.0.4"
+ pirates "^4.0.4"
+ slash "^3.0.0"
+ write-file-atomic "^4.0.2"
+
+"@jest/types@^26.6.2":
+ version "26.6.2"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
+ integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==
+ dependencies:
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ "@types/istanbul-reports" "^3.0.0"
+ "@types/node" "*"
+ "@types/yargs" "^15.0.0"
+ chalk "^4.0.0"
+
+"@jest/types@^29.6.3":
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59"
+ integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==
+ dependencies:
+ "@jest/schemas" "^29.6.3"
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ "@types/istanbul-reports" "^3.0.0"
+ "@types/node" "*"
+ "@types/yargs" "^17.0.8"
+ chalk "^4.0.0"
+
+"@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5":
+ version "0.3.5"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
+ integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
+ dependencies:
+ "@jridgewell/set-array" "^1.2.1"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
+"@jridgewell/resolve-uri@^3.1.0":
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
+ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
+"@jridgewell/set-array@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
+ integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
+
+"@jridgewell/source-map@^0.3.3":
+ version "0.3.6"
+ resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a"
+ integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.25"
+
+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
+ version "1.4.15"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
+ integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
+
+"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+ version "0.3.25"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
+ integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.1.0"
+ "@jridgewell/sourcemap-codec" "^1.4.14"
+
+"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
+ version "5.1.1-v1"
+ resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"
+ integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==
+ dependencies:
+ eslint-scope "5.1.1"
+
+"@nodelib/fs.scandir@2.1.5":
+ version "2.1.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+ integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+ dependencies:
+ "@nodelib/fs.stat" "2.0.5"
+ run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+ integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+ integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+ dependencies:
+ "@nodelib/fs.scandir" "2.1.5"
+ fastq "^1.6.0"
+
+"@react-native-community/cli-clean@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-13.6.4.tgz#53c07c6f2834a971dc40eab290edcf8ccc5d1e00"
+ integrity sha512-nS1BJ+2Z+aLmqePxB4AYgJ+C/bgQt02xAgSYtCUv+lneRBGhL2tHRrK8/Iolp0y+yQoUtHHf4txYi90zGXLVfw==
+ dependencies:
+ "@react-native-community/cli-tools" "13.6.4"
+ chalk "^4.1.2"
+ execa "^5.0.0"
+ fast-glob "^3.3.2"
+
+"@react-native-community/cli-config@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-config/-/cli-config-13.6.4.tgz#3004c7bca55cb384b3a99c38c1a48dad24533237"
+ integrity sha512-GGK415WoTx1R9FXtfb/cTnan9JIWwSm+a5UCuFd6+suzS0oIt1Md1vCzjNh6W1CK3b43rZC2e+3ZU7Ljd7YtyQ==
+ dependencies:
+ "@react-native-community/cli-tools" "13.6.4"
+ chalk "^4.1.2"
+ cosmiconfig "^5.1.0"
+ deepmerge "^4.3.0"
+ fast-glob "^3.3.2"
+ joi "^17.2.1"
+
+"@react-native-community/cli-debugger-ui@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-13.6.4.tgz#3881b9cfe14e66b3ee827a84f19ca9d0283fd764"
+ integrity sha512-9Gs31s6tA1kuEo69ay9qLgM3x2gsN/RI994DCUKnFSW+qSusQJyyrmfllR2mGU3Wl1W09/nYpIg87W9JPf5y4A==
+ dependencies:
+ serve-static "^1.13.1"
+
+"@react-native-community/cli-doctor@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-13.6.4.tgz#07e5c2f163807e61ce0ba12901903e591177e3d3"
+ integrity sha512-lWOXCISH/cHtLvO0cWTr+IPSzA54FewVOw7MoCMEvWusH+1n7c3hXTAve78mLozGQ7iuUufkHFWwKf3dzOkflQ==
+ dependencies:
+ "@react-native-community/cli-config" "13.6.4"
+ "@react-native-community/cli-platform-android" "13.6.4"
+ "@react-native-community/cli-platform-apple" "13.6.4"
+ "@react-native-community/cli-platform-ios" "13.6.4"
+ "@react-native-community/cli-tools" "13.6.4"
+ chalk "^4.1.2"
+ command-exists "^1.2.8"
+ deepmerge "^4.3.0"
+ envinfo "^7.10.0"
+ execa "^5.0.0"
+ hermes-profile-transformer "^0.0.6"
+ node-stream-zip "^1.9.1"
+ ora "^5.4.1"
+ semver "^7.5.2"
+ strip-ansi "^5.2.0"
+ wcwidth "^1.0.1"
+ yaml "^2.2.1"
+
+"@react-native-community/cli-hermes@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-13.6.4.tgz#6d3e9b5c251461e9bb35b04110544db8a4f5968f"
+ integrity sha512-VIAufA/2wTccbMYBT9o+mQs9baOEpTxCiIdWeVdkPWKzIwtKsLpDZJlUqj4r4rI66mwjFyQ60PhwSzEJ2ApFeQ==
+ dependencies:
+ "@react-native-community/cli-platform-android" "13.6.4"
+ "@react-native-community/cli-tools" "13.6.4"
+ chalk "^4.1.2"
+ hermes-profile-transformer "^0.0.6"
+
+"@react-native-community/cli-platform-android@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-13.6.4.tgz#78ab4c840f4f1f5252ad2fcc5a55f7681ec458cb"
+ integrity sha512-WhknYwIobKKCqaGCN3BzZEQHTbaZTDiGvcXzevvN867ldfaGdtbH0DVqNunbPoV1RNzeV9qKoQHFdWBkg83tpg==
+ dependencies:
+ "@react-native-community/cli-tools" "13.6.4"
+ chalk "^4.1.2"
+ execa "^5.0.0"
+ fast-glob "^3.3.2"
+ fast-xml-parser "^4.2.4"
+ logkitty "^0.7.1"
+
+"@react-native-community/cli-platform-apple@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-apple/-/cli-platform-apple-13.6.4.tgz#4912eaf519800a957745192718822b94655c8119"
+ integrity sha512-TLBiotdIz0veLbmvNQIdUv9fkBx7m34ANGYqr5nH7TFxdmey+Z+omoBqG/HGpvyR7d0AY+kZzzV4k+HkYHM/aQ==
+ dependencies:
+ "@react-native-community/cli-tools" "13.6.4"
+ chalk "^4.1.2"
+ execa "^5.0.0"
+ fast-glob "^3.3.2"
+ fast-xml-parser "^4.0.12"
+ ora "^5.4.1"
+
+"@react-native-community/cli-platform-ios@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-13.6.4.tgz#96ec915c6df23b2b7b7e0d8cb3db7368e448d620"
+ integrity sha512-8Dlva8RY+MY5nhWAj6V7voG3+JOEzDTJmD0FHqL+4p0srvr9v7IEVcxfw5lKBDIUNd0OMAHNevGA+cyz1J60jg==
+ dependencies:
+ "@react-native-community/cli-platform-apple" "13.6.4"
+
+"@react-native-community/cli-server-api@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-13.6.4.tgz#6bcec7ae387fc3aeb3e78f62561a91962e6fadf7"
+ integrity sha512-D2qSuYCFwrrUJUM0SDc9l3lEhU02yjf+9Peri/xhspzAhALnsf6Z/H7BCjddMV42g9/eY33LqiGyN5chr83a+g==
+ dependencies:
+ "@react-native-community/cli-debugger-ui" "13.6.4"
+ "@react-native-community/cli-tools" "13.6.4"
+ compression "^1.7.1"
+ connect "^3.6.5"
+ errorhandler "^1.5.1"
+ nocache "^3.0.1"
+ pretty-format "^26.6.2"
+ serve-static "^1.13.1"
+ ws "^7.5.1"
+
+"@react-native-community/cli-tools@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-13.6.4.tgz#ab396604b6dcf215790807fe89656e779b11f0ec"
+ integrity sha512-N4oHLLbeTdg8opqJozjClmuTfazo1Mt+oxU7mr7m45VCsFgBqTF70Uwad289TM/3l44PP679NRMAHVYqpIRYtQ==
+ dependencies:
+ appdirsjs "^1.2.4"
+ chalk "^4.1.2"
+ execa "^5.0.0"
+ find-up "^5.0.0"
+ mime "^2.4.1"
+ node-fetch "^2.6.0"
+ open "^6.2.0"
+ ora "^5.4.1"
+ semver "^7.5.2"
+ shell-quote "^1.7.3"
+ sudo-prompt "^9.0.0"
+
+"@react-native-community/cli-types@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-13.6.4.tgz#e499a3691ee597aa4b93196ff182a4782fae7afb"
+ integrity sha512-NxGCNs4eYtVC8x0wj0jJ/MZLRy8C+B9l8lY8kShuAcvWTv5JXRqmXjg8uK1aA+xikPh0maq4cc/zLw1roroY/A==
+ dependencies:
+ joi "^17.2.1"
+
+"@react-native-community/cli@13.6.4":
+ version "13.6.4"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-13.6.4.tgz#dabe2749470a34533e18aada51d97c94b3568307"
+ integrity sha512-V7rt2N5JY7M4dJFgdNfR164r3hZdR/Z7V54dv85TFQHRbdwF4QrkG+GeagAU54qrkK/OU8OH3AF2+mKuiNWpGA==
+ dependencies:
+ "@react-native-community/cli-clean" "13.6.4"
+ "@react-native-community/cli-config" "13.6.4"
+ "@react-native-community/cli-debugger-ui" "13.6.4"
+ "@react-native-community/cli-doctor" "13.6.4"
+ "@react-native-community/cli-hermes" "13.6.4"
+ "@react-native-community/cli-server-api" "13.6.4"
+ "@react-native-community/cli-tools" "13.6.4"
+ "@react-native-community/cli-types" "13.6.4"
+ chalk "^4.1.2"
+ commander "^9.4.1"
+ deepmerge "^4.3.0"
+ execa "^5.0.0"
+ find-up "^4.1.0"
+ fs-extra "^8.1.0"
+ graceful-fs "^4.1.3"
+ prompts "^2.4.2"
+ semver "^7.5.2"
+
+"@react-native/assets-registry@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.74.81.tgz#76b17f8f79b366ec4f18a0f4e99b7cd466aa5aa7"
+ integrity sha512-ms+D6pJ6l30epm53pwnAislW79LEUHJxWfe1Cu0LWyTTBlg1OFoqXfB3eIbpe4WyH3nrlkQAh0yyk4huT2mCvw==
+
+"@react-native/babel-plugin-codegen@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.81.tgz#80484fb9029038694a92193ae2653529e44aab64"
+ integrity sha512-Bj6g5/xkLMBAdC6665TbD3uCKCQSmLQpGv3gyqya/ydZpv3dDmDXfkGmO4fqTwEMunzu09Sk55st2ipmuXAaAg==
+ dependencies:
+ "@react-native/codegen" "0.74.81"
+
+"@react-native/babel-preset@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.74.81.tgz#80d0b96eef35d671f97eaf223c4d770170d7f23f"
+ integrity sha512-H80B3Y3lBBVC4x9tceTEQq/04lx01gW6ajWCcVbd7sHvGEAxfMFEZUmVZr0451Cafn02wVnDJ8psto1F+0w5lw==
+ dependencies:
+ "@babel/core" "^7.20.0"
+ "@babel/plugin-proposal-async-generator-functions" "^7.0.0"
+ "@babel/plugin-proposal-class-properties" "^7.18.0"
+ "@babel/plugin-proposal-export-default-from" "^7.0.0"
+ "@babel/plugin-proposal-logical-assignment-operators" "^7.18.0"
+ "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.0"
+ "@babel/plugin-proposal-numeric-separator" "^7.0.0"
+ "@babel/plugin-proposal-object-rest-spread" "^7.20.0"
+ "@babel/plugin-proposal-optional-catch-binding" "^7.0.0"
+ "@babel/plugin-proposal-optional-chaining" "^7.20.0"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+ "@babel/plugin-syntax-export-default-from" "^7.0.0"
+ "@babel/plugin-syntax-flow" "^7.18.0"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0"
+ "@babel/plugin-syntax-optional-chaining" "^7.0.0"
+ "@babel/plugin-transform-arrow-functions" "^7.0.0"
+ "@babel/plugin-transform-async-to-generator" "^7.20.0"
+ "@babel/plugin-transform-block-scoping" "^7.0.0"
+ "@babel/plugin-transform-classes" "^7.0.0"
+ "@babel/plugin-transform-computed-properties" "^7.0.0"
+ "@babel/plugin-transform-destructuring" "^7.20.0"
+ "@babel/plugin-transform-flow-strip-types" "^7.20.0"
+ "@babel/plugin-transform-function-name" "^7.0.0"
+ "@babel/plugin-transform-literals" "^7.0.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.0.0"
+ "@babel/plugin-transform-named-capturing-groups-regex" "^7.0.0"
+ "@babel/plugin-transform-parameters" "^7.0.0"
+ "@babel/plugin-transform-private-methods" "^7.22.5"
+ "@babel/plugin-transform-private-property-in-object" "^7.22.11"
+ "@babel/plugin-transform-react-display-name" "^7.0.0"
+ "@babel/plugin-transform-react-jsx" "^7.0.0"
+ "@babel/plugin-transform-react-jsx-self" "^7.0.0"
+ "@babel/plugin-transform-react-jsx-source" "^7.0.0"
+ "@babel/plugin-transform-runtime" "^7.0.0"
+ "@babel/plugin-transform-shorthand-properties" "^7.0.0"
+ "@babel/plugin-transform-spread" "^7.0.0"
+ "@babel/plugin-transform-sticky-regex" "^7.0.0"
+ "@babel/plugin-transform-typescript" "^7.5.0"
+ "@babel/plugin-transform-unicode-regex" "^7.0.0"
+ "@babel/template" "^7.0.0"
+ "@react-native/babel-plugin-codegen" "0.74.81"
+ babel-plugin-transform-flow-enums "^0.0.2"
+ react-refresh "^0.14.0"
+
+"@react-native/codegen@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/codegen/-/codegen-0.74.81.tgz#1025ffd41f2b4710fd700c9e8e85210b9651a7c4"
+ integrity sha512-hhXo4ccv2lYWaJrZDsdbRTZ5SzSOdyZ0MY6YXwf3xEFLuSunbUMu17Rz5LXemKXlpVx4KEgJ/TDc2pPVaRPZgA==
+ dependencies:
+ "@babel/parser" "^7.20.0"
+ glob "^7.1.1"
+ hermes-parser "0.19.1"
+ invariant "^2.2.4"
+ jscodeshift "^0.14.0"
+ mkdirp "^0.5.1"
+ nullthrows "^1.1.1"
+
+"@react-native/community-cli-plugin@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.81.tgz#4177207374942c52a86ad52c8c915f46729305ab"
+ integrity sha512-ezPOwPxbDgrBZLJJMcXryXJXjv3VWt+Mt4jRZiEtvy6pAoi2owSH0b178T5cEZaWsxQN0BbyJ7F/xJsNiF4z0Q==
+ dependencies:
+ "@react-native-community/cli-server-api" "13.6.4"
+ "@react-native-community/cli-tools" "13.6.4"
+ "@react-native/dev-middleware" "0.74.81"
+ "@react-native/metro-babel-transformer" "0.74.81"
+ chalk "^4.0.0"
+ execa "^5.1.1"
+ metro "^0.80.3"
+ metro-config "^0.80.3"
+ metro-core "^0.80.3"
+ node-fetch "^2.2.0"
+ querystring "^0.2.1"
+ readline "^1.3.0"
+
+"@react-native/debugger-frontend@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/debugger-frontend/-/debugger-frontend-0.74.81.tgz#17cefe2b3ff485071bd30d819995867fd145da27"
+ integrity sha512-HCYF1/88AfixG75558HkNh9wcvGweRaSZGBA71KoZj03umXM8XJy0/ZpacGOml2Fwiqpil72gi6uU+rypcc/vw==
+
+"@react-native/dev-middleware@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/dev-middleware/-/dev-middleware-0.74.81.tgz#120ab62982a48cba90c7724d099ddaa50184c200"
+ integrity sha512-x2IpvUJN1LJE0WmPsSfQIbQaa9xwH+2VDFOUrzuO9cbQap8rNfZpcvVNbrZgrlKbgS4LXbbsj6VSL8b6SnMKMA==
+ dependencies:
+ "@isaacs/ttlcache" "^1.4.1"
+ "@react-native/debugger-frontend" "0.74.81"
+ "@rnx-kit/chromium-edge-launcher" "^1.0.0"
+ chrome-launcher "^0.15.2"
+ connect "^3.6.5"
+ debug "^2.2.0"
+ node-fetch "^2.2.0"
+ nullthrows "^1.1.1"
+ open "^7.0.3"
+ selfsigned "^2.4.1"
+ serve-static "^1.13.1"
+ temp-dir "^2.0.0"
+ ws "^6.2.2"
+
+"@react-native/eslint-config@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/eslint-config/-/eslint-config-0.74.81.tgz#36db043a03a024c0e5daf14707c86acb8218bcd5"
+ integrity sha512-XIBjvKxNJYzON6dInZcpuVDRNGC4QYXtwFu6KUVpnPbWVmOSP1PzUVy8R+y0Vh2FOpYnKmoCLlMU1V4evnZmpw==
+ dependencies:
+ "@babel/core" "^7.20.0"
+ "@babel/eslint-parser" "^7.20.0"
+ "@react-native/eslint-plugin" "0.74.81"
+ "@typescript-eslint/eslint-plugin" "^6.7.4"
+ "@typescript-eslint/parser" "^6.7.4"
+ eslint-config-prettier "^8.5.0"
+ eslint-plugin-eslint-comments "^3.2.0"
+ eslint-plugin-ft-flow "^2.0.1"
+ eslint-plugin-jest "^26.5.3"
+ eslint-plugin-prettier "^4.2.1"
+ eslint-plugin-react "^7.30.1"
+ eslint-plugin-react-hooks "^4.6.0"
+ eslint-plugin-react-native "^4.0.0"
+
+"@react-native/eslint-plugin@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/eslint-plugin/-/eslint-plugin-0.74.81.tgz#ac53da7c41a35948b0f9d01d88d2a858e879edb1"
+ integrity sha512-vlbLJ38MFJzcEgNxNswjgDRELvZX5e4SmGhtN9N1ZQpXLkgo3hs+l2m4ulSpKhSmqpbacB5XbuTTMgKOsLj/5w==
+
+"@react-native/gradle-plugin@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.74.81.tgz#aac01999b1005bba3213f504deee7efaadb62c1e"
+ integrity sha512-7YQ4TLnqfe2kplWWzBWO6k0rPSrWEbuEiRXSJNZQCtCk+t2YX985G62p/9jWm3sGLN4UTcpDXaFNTTPBvlycoQ==
+
+"@react-native/js-polyfills@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/js-polyfills/-/js-polyfills-0.74.81.tgz#64780497be4ecbff1b27076294e3ebd7df1ba485"
+ integrity sha512-o4MiR+/kkHoeoQ/zPwt81LnTm6pqdg0wOhU7S7vIZUqzJ7YUpnpaAvF+/z7HzUOPudnavoCN0wvcZPe/AMEyCA==
+
+"@react-native/metro-babel-transformer@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.81.tgz#f724eab91e6de82f8d098e6de57f25bb7501d2d6"
+ integrity sha512-PVcMjj23poAK6Uemflz4MIJdEpONpjqF7JASNqqQkY6wfDdaIiZSNk8EBCWKb0t7nKqhMvtTq11DMzYJ0JFITg==
+ dependencies:
+ "@babel/core" "^7.20.0"
+ "@react-native/babel-preset" "0.74.81"
+ hermes-parser "0.19.1"
+ nullthrows "^1.1.1"
+
+"@react-native/metro-config@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/metro-config/-/metro-config-0.74.81.tgz#3ed605c0bb51081905171af3e0326abd3adc0b27"
+ integrity sha512-VInZ60cXC/e5MY7kMlrMRn6Mhpj05hJfiJngRKy8BsWnXJNQMv3iggar+XnfSh98saLw1yG96dO3G6s2WQhzOg==
+ dependencies:
+ "@react-native/js-polyfills" "0.74.81"
+ "@react-native/metro-babel-transformer" "0.74.81"
+ metro-config "^0.80.3"
+ metro-runtime "^0.80.3"
+
+"@react-native/normalize-colors@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.74.81.tgz#0b7c440b6e126f79036cbe74a88791aba72b9fcf"
+ integrity sha512-g3YvkLO7UsSWiDfYAU+gLhRHtEpUyz732lZB+N8IlLXc5MnfXHC8GKneDGY3Mh52I3gBrs20o37D5viQX9E1CA==
+
+"@react-native/typescript-config@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/typescript-config/-/typescript-config-0.74.81.tgz#a249b6a21b577d572a0a70056d7c48a55fd6662f"
+ integrity sha512-jk4LJUKdRYmXxxpebRSW8mK9xJPW90W6BE1IE9LdFi0exdsnVv5gXM9QylG+9kDVZj3bltMuMVdijWnU7SRNbg==
+
+"@react-native/virtualized-lists@0.74.81":
+ version "0.74.81"
+ resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.74.81.tgz#8e43d4c72ec561754491eae731f40877f03d05fb"
+ integrity sha512-5jF9S10Ug2Wl+L/0+O8WmbC726sMMX8jk/1JrvDDK+0DRLMobfjLc1L26fONlVBF7lE5ctqvKZ9TlKdhPTNOZg==
+ dependencies:
+ invariant "^2.2.4"
+ nullthrows "^1.1.1"
+
+"@rnx-kit/chromium-edge-launcher@^1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@rnx-kit/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz#c0df8ea00a902c7a417cd9655aab06de398b939c"
+ integrity sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg==
+ dependencies:
+ "@types/node" "^18.0.0"
+ escape-string-regexp "^4.0.0"
+ is-wsl "^2.2.0"
+ lighthouse-logger "^1.0.0"
+ mkdirp "^1.0.4"
+ rimraf "^3.0.2"
+
+"@sideway/address@^4.1.5":
+ version "4.1.5"
+ resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5"
+ integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==
+ dependencies:
+ "@hapi/hoek" "^9.0.0"
+
+"@sideway/formula@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
+ integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
+
+"@sideway/pinpoint@^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
+ integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
+
+"@sinclair/typebox@^0.27.8":
+ version "0.27.8"
+ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
+ integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
+
+"@sinonjs/commons@^3.0.0":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd"
+ integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==
+ dependencies:
+ type-detect "4.0.8"
+
+"@sinonjs/fake-timers@^10.0.2":
+ version "10.3.0"
+ resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66"
+ integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==
+ dependencies:
+ "@sinonjs/commons" "^3.0.0"
+
+"@types/babel__core@^7.1.14":
+ version "7.20.5"
+ resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
+ integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==
+ dependencies:
+ "@babel/parser" "^7.20.7"
+ "@babel/types" "^7.20.7"
+ "@types/babel__generator" "*"
+ "@types/babel__template" "*"
+ "@types/babel__traverse" "*"
+
+"@types/babel__generator@*":
+ version "7.6.8"
+ resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab"
+ integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@types/babel__template@*":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f"
+ integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==
+ dependencies:
+ "@babel/parser" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6":
+ version "7.20.5"
+ resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd"
+ integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==
+ dependencies:
+ "@babel/types" "^7.20.7"
+
+"@types/graceful-fs@^4.1.3":
+ version "4.1.9"
+ resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4"
+ integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==
+ dependencies:
+ "@types/node" "*"
+
+"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7"
+ integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==
+
+"@types/istanbul-lib-report@*":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf"
+ integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==
+ dependencies:
+ "@types/istanbul-lib-coverage" "*"
+
+"@types/istanbul-reports@^3.0.0":
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54"
+ integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==
+ dependencies:
+ "@types/istanbul-lib-report" "*"
+
+"@types/json-schema@^7.0.12", "@types/json-schema@^7.0.9":
+ version "7.0.15"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
+ integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
+
+"@types/node-forge@^1.3.0":
+ version "1.3.11"
+ resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da"
+ integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==
+ dependencies:
+ "@types/node" "*"
+
+"@types/node@*":
+ version "20.11.27"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.27.tgz#debe5cfc8a507dd60fe2a3b4875b1604f215c2ac"
+ integrity sha512-qyUZfMnCg1KEz57r7pzFtSGt49f6RPkPBis3Vo4PbS7roQEDn22hiHzl/Lo1q4i4hDEgBJmBF/NTNg2XR0HbFg==
+ dependencies:
+ undici-types "~5.26.4"
+
+"@types/node@^18.0.0":
+ version "18.19.33"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.33.tgz#98cd286a1b8a5e11aa06623210240bcc28e95c48"
+ integrity sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==
+ dependencies:
+ undici-types "~5.26.4"
+
+"@types/prop-types@*":
+ version "15.7.11"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563"
+ integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==
+
+"@types/react-test-renderer@^18.0.0":
+ version "18.0.7"
+ resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.7.tgz#2cfe657adb3688cdf543995eceb2e062b5a68728"
+ integrity sha512-1+ANPOWc6rB3IkSnElhjv6VLlKg2dSv/OWClUyZimbLsQyBn8Js9Vtdsi3UICJ2rIQ3k2la06dkB+C92QfhKmg==
+ dependencies:
+ "@types/react" "*"
+
+"@types/react@*", "@types/react@^18.2.6":
+ version "18.2.66"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.66.tgz#d2eafc8c4e70939c5432221adb23d32d76bfe451"
+ integrity sha512-OYTmMI4UigXeFMF/j4uv0lBBEbongSgptPrHBxqME44h9+yNov+oL6Z3ocJKo0WyXR84sQUNeyIp9MRfckvZpg==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
+"@types/scheduler@*":
+ version "0.16.8"
+ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff"
+ integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==
+
+"@types/semver@^7.3.12", "@types/semver@^7.5.0":
+ version "7.5.8"
+ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
+ integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
+
+"@types/stack-utils@^2.0.0":
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8"
+ integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==
+
+"@types/yargs-parser@*":
+ version "21.0.3"
+ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
+ integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==
+
+"@types/yargs@^15.0.0":
+ version "15.0.19"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.19.tgz#328fb89e46109ecbdb70c295d96ff2f46dfd01b9"
+ integrity sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==
+ dependencies:
+ "@types/yargs-parser" "*"
+
+"@types/yargs@^17.0.8":
+ version "17.0.32"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229"
+ integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==
+ dependencies:
+ "@types/yargs-parser" "*"
+
+"@typescript-eslint/eslint-plugin@^6.7.4":
+ version "6.21.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3"
+ integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==
+ dependencies:
+ "@eslint-community/regexpp" "^4.5.1"
+ "@typescript-eslint/scope-manager" "6.21.0"
+ "@typescript-eslint/type-utils" "6.21.0"
+ "@typescript-eslint/utils" "6.21.0"
+ "@typescript-eslint/visitor-keys" "6.21.0"
+ debug "^4.3.4"
+ graphemer "^1.4.0"
+ ignore "^5.2.4"
+ natural-compare "^1.4.0"
+ semver "^7.5.4"
+ ts-api-utils "^1.0.1"
+
+"@typescript-eslint/parser@^6.7.4":
+ version "6.21.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b"
+ integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==
+ dependencies:
+ "@typescript-eslint/scope-manager" "6.21.0"
+ "@typescript-eslint/types" "6.21.0"
+ "@typescript-eslint/typescript-estree" "6.21.0"
+ "@typescript-eslint/visitor-keys" "6.21.0"
+ debug "^4.3.4"
+
+"@typescript-eslint/scope-manager@5.62.0":
+ version "5.62.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c"
+ integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==
+ dependencies:
+ "@typescript-eslint/types" "5.62.0"
+ "@typescript-eslint/visitor-keys" "5.62.0"
+
+"@typescript-eslint/scope-manager@6.21.0":
+ version "6.21.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1"
+ integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==
+ dependencies:
+ "@typescript-eslint/types" "6.21.0"
+ "@typescript-eslint/visitor-keys" "6.21.0"
+
+"@typescript-eslint/type-utils@6.21.0":
+ version "6.21.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e"
+ integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==
+ dependencies:
+ "@typescript-eslint/typescript-estree" "6.21.0"
+ "@typescript-eslint/utils" "6.21.0"
+ debug "^4.3.4"
+ ts-api-utils "^1.0.1"
+
+"@typescript-eslint/types@5.62.0":
+ version "5.62.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
+ integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
+
+"@typescript-eslint/types@6.21.0":
+ version "6.21.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d"
+ integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==
+
+"@typescript-eslint/typescript-estree@5.62.0":
+ version "5.62.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b"
+ integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==
+ dependencies:
+ "@typescript-eslint/types" "5.62.0"
+ "@typescript-eslint/visitor-keys" "5.62.0"
+ debug "^4.3.4"
+ globby "^11.1.0"
+ is-glob "^4.0.3"
+ semver "^7.3.7"
+ tsutils "^3.21.0"
+
+"@typescript-eslint/typescript-estree@6.21.0":
+ version "6.21.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46"
+ integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==
+ dependencies:
+ "@typescript-eslint/types" "6.21.0"
+ "@typescript-eslint/visitor-keys" "6.21.0"
+ debug "^4.3.4"
+ globby "^11.1.0"
+ is-glob "^4.0.3"
+ minimatch "9.0.3"
+ semver "^7.5.4"
+ ts-api-utils "^1.0.1"
+
+"@typescript-eslint/utils@6.21.0":
+ version "6.21.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134"
+ integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.4.0"
+ "@types/json-schema" "^7.0.12"
+ "@types/semver" "^7.5.0"
+ "@typescript-eslint/scope-manager" "6.21.0"
+ "@typescript-eslint/types" "6.21.0"
+ "@typescript-eslint/typescript-estree" "6.21.0"
+ semver "^7.5.4"
+
+"@typescript-eslint/utils@^5.10.0":
+ version "5.62.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86"
+ integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.2.0"
+ "@types/json-schema" "^7.0.9"
+ "@types/semver" "^7.3.12"
+ "@typescript-eslint/scope-manager" "5.62.0"
+ "@typescript-eslint/types" "5.62.0"
+ "@typescript-eslint/typescript-estree" "5.62.0"
+ eslint-scope "^5.1.1"
+ semver "^7.3.7"
+
+"@typescript-eslint/visitor-keys@5.62.0":
+ version "5.62.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e"
+ integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==
+ dependencies:
+ "@typescript-eslint/types" "5.62.0"
+ eslint-visitor-keys "^3.3.0"
+
+"@typescript-eslint/visitor-keys@6.21.0":
+ version "6.21.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47"
+ integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==
+ dependencies:
+ "@typescript-eslint/types" "6.21.0"
+ eslint-visitor-keys "^3.4.1"
+
+"@ungap/structured-clone@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
+ integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
+
+abort-controller@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
+ integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
+ dependencies:
+ event-target-shim "^5.0.0"
+
+accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
+ integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
+ dependencies:
+ mime-types "~2.1.34"
+ negotiator "0.6.3"
+
+acorn-jsx@^5.3.2:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^8.8.2, acorn@^8.9.0:
+ version "8.11.3"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
+ integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
+
+ajv@^6.12.4:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+anser@^1.4.9:
+ version "1.4.10"
+ resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b"
+ integrity sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==
+
+ansi-escapes@^4.2.1:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
+ integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
+ dependencies:
+ type-fest "^0.21.3"
+
+ansi-fragments@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-fragments/-/ansi-fragments-0.2.1.tgz#24409c56c4cc37817c3d7caa99d8969e2de5a05e"
+ integrity sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==
+ dependencies:
+ colorette "^1.0.7"
+ slice-ansi "^2.0.0"
+ strip-ansi "^5.0.0"
+
+ansi-regex@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
+ integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
+
+ansi-regex@^5.0.0, ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^3.2.0, ansi-styles@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+ dependencies:
+ color-convert "^1.9.0"
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+ansi-styles@^5.0.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
+ integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
+
+anymatch@^3.0.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
+ integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
+ dependencies:
+ normalize-path "^3.0.0"
+ picomatch "^2.0.4"
+
+appdirsjs@^1.2.4:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.7.tgz#50b4b7948a26ba6090d4aede2ae2dc2b051be3b3"
+ integrity sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==
+
+argparse@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+ dependencies:
+ sprintf-js "~1.0.2"
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+array-buffer-byte-length@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f"
+ integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==
+ dependencies:
+ call-bind "^1.0.5"
+ is-array-buffer "^3.0.4"
+
+array-includes@^3.1.6, array-includes@^3.1.7:
+ version "3.1.7"
+ resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda"
+ integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+ get-intrinsic "^1.2.1"
+ is-string "^1.0.7"
+
+array-union@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
+ integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
+
+array.prototype.findlast@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.4.tgz#eeb9e45fc894055c82e5675c463e8077b827ad36"
+ integrity sha512-BMtLxpV+8BD+6ZPFIWmnUBpQoy+A+ujcg4rhp2iwCRJYA7PEh2MS4NL3lz8EiDlLrJPp2hg9qWihr5pd//jcGw==
+ dependencies:
+ call-bind "^1.0.5"
+ define-properties "^1.2.1"
+ es-abstract "^1.22.3"
+ es-errors "^1.3.0"
+ es-shim-unscopables "^1.0.2"
+
+array.prototype.flat@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18"
+ integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+ es-shim-unscopables "^1.0.0"
+
+array.prototype.flatmap@^1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527"
+ integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+ es-shim-unscopables "^1.0.0"
+
+array.prototype.toreversed@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz#b989a6bf35c4c5051e1dc0325151bf8088954eba"
+ integrity sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+ es-shim-unscopables "^1.0.0"
+
+array.prototype.tosorted@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz#c8c89348337e51b8a3c48a9227f9ce93ceedcba8"
+ integrity sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==
+ dependencies:
+ call-bind "^1.0.5"
+ define-properties "^1.2.1"
+ es-abstract "^1.22.3"
+ es-errors "^1.1.0"
+ es-shim-unscopables "^1.0.2"
+
+arraybuffer.prototype.slice@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6"
+ integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==
+ dependencies:
+ array-buffer-byte-length "^1.0.1"
+ call-bind "^1.0.5"
+ define-properties "^1.2.1"
+ es-abstract "^1.22.3"
+ es-errors "^1.2.1"
+ get-intrinsic "^1.2.3"
+ is-array-buffer "^3.0.4"
+ is-shared-array-buffer "^1.0.2"
+
+asap@~2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
+ integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
+
+ast-types@0.15.2:
+ version "0.15.2"
+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.15.2.tgz#39ae4809393c4b16df751ee563411423e85fb49d"
+ integrity sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==
+ dependencies:
+ tslib "^2.0.1"
+
+astral-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
+ integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
+
+async-limiter@~1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
+ integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
+
+asynciterator.prototype@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz#8c5df0514936cdd133604dfcc9d3fb93f09b2b62"
+ integrity sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==
+ dependencies:
+ has-symbols "^1.0.3"
+
+available-typed-arrays@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846"
+ integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==
+ dependencies:
+ possible-typed-array-names "^1.0.0"
+
+babel-core@^7.0.0-bridge.0:
+ version "7.0.0-bridge.0"
+ resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece"
+ integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==
+
+babel-jest@^29.6.3, babel-jest@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5"
+ integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==
+ dependencies:
+ "@jest/transform" "^29.7.0"
+ "@types/babel__core" "^7.1.14"
+ babel-plugin-istanbul "^6.1.1"
+ babel-preset-jest "^29.6.3"
+ chalk "^4.0.0"
+ graceful-fs "^4.2.9"
+ slash "^3.0.0"
+
+babel-plugin-istanbul@^6.1.1:
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73"
+ integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@istanbuljs/load-nyc-config" "^1.0.0"
+ "@istanbuljs/schema" "^0.1.2"
+ istanbul-lib-instrument "^5.0.4"
+ test-exclude "^6.0.0"
+
+babel-plugin-jest-hoist@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626"
+ integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==
+ dependencies:
+ "@babel/template" "^7.3.3"
+ "@babel/types" "^7.3.3"
+ "@types/babel__core" "^7.1.14"
+ "@types/babel__traverse" "^7.0.6"
+
+babel-plugin-polyfill-corejs2@^0.4.8:
+ version "0.4.10"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.10.tgz#276f41710b03a64f6467433cab72cbc2653c38b1"
+ integrity sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ==
+ dependencies:
+ "@babel/compat-data" "^7.22.6"
+ "@babel/helper-define-polyfill-provider" "^0.6.1"
+ semver "^6.3.1"
+
+babel-plugin-polyfill-corejs3@^0.9.0:
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz#9eea32349d94556c2ad3ab9b82ebb27d4bf04a81"
+ integrity sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==
+ dependencies:
+ "@babel/helper-define-polyfill-provider" "^0.5.0"
+ core-js-compat "^3.34.0"
+
+babel-plugin-polyfill-regenerator@^0.5.5:
+ version "0.5.5"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz#8b0c8fc6434239e5d7b8a9d1f832bb2b0310f06a"
+ integrity sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==
+ dependencies:
+ "@babel/helper-define-polyfill-provider" "^0.5.0"
+
+babel-plugin-transform-flow-enums@^0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz#d1d0cc9bdc799c850ca110d0ddc9f21b9ec3ef25"
+ integrity sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==
+ dependencies:
+ "@babel/plugin-syntax-flow" "^7.12.1"
+
+babel-preset-current-node-syntax@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b"
+ integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==
+ dependencies:
+ "@babel/plugin-syntax-async-generators" "^7.8.4"
+ "@babel/plugin-syntax-bigint" "^7.8.3"
+ "@babel/plugin-syntax-class-properties" "^7.8.3"
+ "@babel/plugin-syntax-import-meta" "^7.8.3"
+ "@babel/plugin-syntax-json-strings" "^7.8.3"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+ "@babel/plugin-syntax-numeric-separator" "^7.8.3"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+ "@babel/plugin-syntax-top-level-await" "^7.8.3"
+
+babel-preset-jest@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c"
+ integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==
+ dependencies:
+ babel-plugin-jest-hoist "^29.6.3"
+ babel-preset-current-node-syntax "^1.0.0"
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+base64-js@^1.3.1, base64-js@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+ integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
+bl@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
+ integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
+ dependencies:
+ buffer "^5.5.0"
+ inherits "^2.0.4"
+ readable-stream "^3.4.0"
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
+braces@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+ integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+ dependencies:
+ fill-range "^7.0.1"
+
+browserslist@^4.22.2, browserslist@^4.22.3:
+ version "4.23.0"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab"
+ integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==
+ dependencies:
+ caniuse-lite "^1.0.30001587"
+ electron-to-chromium "^1.4.668"
+ node-releases "^2.0.14"
+ update-browserslist-db "^1.0.13"
+
+bser@2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
+ integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==
+ dependencies:
+ node-int64 "^0.4.0"
+
+buffer-from@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+ integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+buffer@^5.5.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+ integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.1.13"
+
+bytes@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+ integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==
+
+call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
+ integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
+ dependencies:
+ es-define-property "^1.0.0"
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ get-intrinsic "^1.2.4"
+ set-function-length "^1.2.1"
+
+caller-callsite@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
+ integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==
+ dependencies:
+ callsites "^2.0.0"
+
+caller-path@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
+ integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==
+ dependencies:
+ caller-callsite "^2.0.0"
+
+callsites@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+ integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==
+
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+camelcase@^5.0.0, camelcase@^5.3.1:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+ integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+camelcase@^6.2.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
+ integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
+
+caniuse-lite@^1.0.30001587:
+ version "1.0.30001597"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz#8be94a8c1d679de23b22fbd944232aa1321639e6"
+ integrity sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==
+
+chalk@^2.4.2:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+ dependencies:
+ ansi-styles "^3.2.1"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.3.0"
+
+chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+char-regex@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
+ integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
+
+chrome-launcher@^0.15.2:
+ version "0.15.2"
+ resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.15.2.tgz#4e6404e32200095fdce7f6a1e1004f9bd36fa5da"
+ integrity sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==
+ dependencies:
+ "@types/node" "*"
+ escape-string-regexp "^4.0.0"
+ is-wsl "^2.2.0"
+ lighthouse-logger "^1.0.0"
+
+ci-info@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
+ integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
+
+ci-info@^3.2.0:
+ version "3.9.0"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4"
+ integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==
+
+cjs-module-lexer@^1.0.0:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107"
+ integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==
+
+cli-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+ integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+ dependencies:
+ restore-cursor "^3.1.0"
+
+cli-spinners@^2.5.0:
+ version "2.9.2"
+ resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41"
+ integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==
+
+cliui@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
+ integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.0"
+ wrap-ansi "^6.2.0"
+
+cliui@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
+ integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.1"
+ wrap-ansi "^7.0.0"
+
+clone-deep@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
+ integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
+ dependencies:
+ is-plain-object "^2.0.4"
+ kind-of "^6.0.2"
+ shallow-clone "^3.0.0"
+
+clone@^1.0.2:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+ integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
+
+co@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+ integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==
+
+collect-v8-coverage@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9"
+ integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==
+
+color-convert@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+ dependencies:
+ color-name "1.1.3"
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+colorette@^1.0.7:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
+ integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
+
+command-exists@^1.2.8:
+ version "1.2.9"
+ resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69"
+ integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==
+
+commander@^2.20.0:
+ version "2.20.3"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commander@^9.4.1:
+ version "9.5.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
+ integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==
+
+commondir@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+ integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
+
+compressible@~2.0.16:
+ version "2.0.18"
+ resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
+ integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
+ dependencies:
+ mime-db ">= 1.43.0 < 2"
+
+compression@^1.7.1:
+ version "1.7.4"
+ resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
+ integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
+ dependencies:
+ accepts "~1.3.5"
+ bytes "3.0.0"
+ compressible "~2.0.16"
+ debug "2.6.9"
+ on-headers "~1.0.2"
+ safe-buffer "5.1.2"
+ vary "~1.1.2"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+connect@^3.6.5:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8"
+ integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==
+ dependencies:
+ debug "2.6.9"
+ finalhandler "1.1.2"
+ parseurl "~1.3.3"
+ utils-merge "1.0.1"
+
+convert-source-map@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
+ integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
+
+core-js-compat@^3.31.0, core-js-compat@^3.34.0:
+ version "3.36.0"
+ resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.36.0.tgz#087679119bc2fdbdefad0d45d8e5d307d45ba190"
+ integrity sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==
+ dependencies:
+ browserslist "^4.22.3"
+
+core-util-is@~1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
+ integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
+
+cosmiconfig@^5.0.5, cosmiconfig@^5.1.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
+ integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
+ dependencies:
+ import-fresh "^2.0.0"
+ is-directory "^0.3.1"
+ js-yaml "^3.13.1"
+ parse-json "^4.0.0"
+
+create-jest@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320"
+ integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ chalk "^4.0.0"
+ exit "^0.1.2"
+ graceful-fs "^4.2.9"
+ jest-config "^29.7.0"
+ jest-util "^29.7.0"
+ prompts "^2.0.1"
+
+cross-spawn@^7.0.2, cross-spawn@^7.0.3:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
+csstype@^3.0.2:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
+ integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
+
+dayjs@^1.8.15:
+ version "1.11.10"
+ resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
+ integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
+
+debug@2.6.9, debug@^2.2.0, debug@^2.6.9:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ dependencies:
+ ms "2.1.2"
+
+decamelize@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+ integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
+
+dedent@^1.0.0:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff"
+ integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==
+
+deep-is@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+ integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+deepmerge@^4.2.2, deepmerge@^4.3.0:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
+ integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
+
+defaults@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a"
+ integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==
+ dependencies:
+ clone "^1.0.2"
+
+define-data-property@^1.0.1, define-data-property@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
+ integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
+ dependencies:
+ es-define-property "^1.0.0"
+ es-errors "^1.3.0"
+ gopd "^1.0.1"
+
+define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
+ integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
+ dependencies:
+ define-data-property "^1.0.1"
+ has-property-descriptors "^1.0.0"
+ object-keys "^1.1.1"
+
+denodeify@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631"
+ integrity sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==
+
+depd@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
+ integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
+
+destroy@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
+ integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
+
+detect-newline@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
+ integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
+
+diff-sequences@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
+ integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
+
+dir-glob@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
+ integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
+ dependencies:
+ path-type "^4.0.0"
+
+doctrine@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
+ integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
+ dependencies:
+ esutils "^2.0.2"
+
+doctrine@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+ dependencies:
+ esutils "^2.0.2"
+
+ee-first@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+ integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
+
+electron-to-chromium@^1.4.668:
+ version "1.4.705"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.705.tgz#ef4f912620bd7c9555a20554ffc568184c0ddceb"
+ integrity sha512-LKqhpwJCLhYId2VVwEzFXWrqQI5n5zBppz1W9ehhTlfYU8CUUW6kClbN8LHF/v7flMgRdETS772nqywJ+ckVAw==
+
+emittery@^0.13.1:
+ version "0.13.1"
+ resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
+ integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+encodeurl@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+ integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
+
+envinfo@^7.10.0:
+ version "7.11.1"
+ resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.1.tgz#2ffef77591057081b0129a8fd8cf6118da1b94e1"
+ integrity sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==
+
+error-ex@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+ dependencies:
+ is-arrayish "^0.2.1"
+
+error-stack-parser@^2.0.6:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286"
+ integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==
+ dependencies:
+ stackframe "^1.3.4"
+
+errorhandler@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/errorhandler/-/errorhandler-1.5.1.tgz#b9ba5d17cf90744cd1e851357a6e75bf806a9a91"
+ integrity sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==
+ dependencies:
+ accepts "~1.3.7"
+ escape-html "~1.0.3"
+
+es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.22.4:
+ version "1.22.5"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.5.tgz#1417df4e97cc55f09bf7e58d1e614bc61cb8df46"
+ integrity sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==
+ dependencies:
+ array-buffer-byte-length "^1.0.1"
+ arraybuffer.prototype.slice "^1.0.3"
+ available-typed-arrays "^1.0.7"
+ call-bind "^1.0.7"
+ es-define-property "^1.0.0"
+ es-errors "^1.3.0"
+ es-set-tostringtag "^2.0.3"
+ es-to-primitive "^1.2.1"
+ function.prototype.name "^1.1.6"
+ get-intrinsic "^1.2.4"
+ get-symbol-description "^1.0.2"
+ globalthis "^1.0.3"
+ gopd "^1.0.1"
+ has-property-descriptors "^1.0.2"
+ has-proto "^1.0.3"
+ has-symbols "^1.0.3"
+ hasown "^2.0.1"
+ internal-slot "^1.0.7"
+ is-array-buffer "^3.0.4"
+ is-callable "^1.2.7"
+ is-negative-zero "^2.0.3"
+ is-regex "^1.1.4"
+ is-shared-array-buffer "^1.0.3"
+ is-string "^1.0.7"
+ is-typed-array "^1.1.13"
+ is-weakref "^1.0.2"
+ object-inspect "^1.13.1"
+ object-keys "^1.1.1"
+ object.assign "^4.1.5"
+ regexp.prototype.flags "^1.5.2"
+ safe-array-concat "^1.1.0"
+ safe-regex-test "^1.0.3"
+ string.prototype.trim "^1.2.8"
+ string.prototype.trimend "^1.0.7"
+ string.prototype.trimstart "^1.0.7"
+ typed-array-buffer "^1.0.2"
+ typed-array-byte-length "^1.0.1"
+ typed-array-byte-offset "^1.0.2"
+ typed-array-length "^1.0.5"
+ unbox-primitive "^1.0.2"
+ which-typed-array "^1.1.14"
+
+es-define-property@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
+ integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
+ dependencies:
+ get-intrinsic "^1.2.4"
+
+es-errors@^1.0.0, es-errors@^1.1.0, es-errors@^1.2.1, es-errors@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
+ integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+
+es-iterator-helpers@^1.0.17:
+ version "1.0.17"
+ resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz#123d1315780df15b34eb181022da43e734388bb8"
+ integrity sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==
+ dependencies:
+ asynciterator.prototype "^1.0.0"
+ call-bind "^1.0.7"
+ define-properties "^1.2.1"
+ es-abstract "^1.22.4"
+ es-errors "^1.3.0"
+ es-set-tostringtag "^2.0.2"
+ function-bind "^1.1.2"
+ get-intrinsic "^1.2.4"
+ globalthis "^1.0.3"
+ has-property-descriptors "^1.0.2"
+ has-proto "^1.0.1"
+ has-symbols "^1.0.3"
+ internal-slot "^1.0.7"
+ iterator.prototype "^1.1.2"
+ safe-array-concat "^1.1.0"
+
+es-set-tostringtag@^2.0.2, es-set-tostringtag@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777"
+ integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==
+ dependencies:
+ get-intrinsic "^1.2.4"
+ has-tostringtag "^1.0.2"
+ hasown "^2.0.1"
+
+es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763"
+ integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==
+ dependencies:
+ hasown "^2.0.0"
+
+es-to-primitive@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
+ integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
+ dependencies:
+ is-callable "^1.1.4"
+ is-date-object "^1.0.1"
+ is-symbol "^1.0.2"
+
+escalade@^3.1.1:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
+ integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
+
+escape-html@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+ integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
+
+escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+escape-string-regexp@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
+ integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
+
+escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-config-prettier@^8.5.0:
+ version "8.10.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11"
+ integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==
+
+eslint-plugin-eslint-comments@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz#9e1cd7b4413526abb313933071d7aba05ca12ffa"
+ integrity sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==
+ dependencies:
+ escape-string-regexp "^1.0.5"
+ ignore "^5.0.5"
+
+eslint-plugin-ft-flow@^2.0.1:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-ft-flow/-/eslint-plugin-ft-flow-2.0.3.tgz#3b3c113c41902bcbacf0e22b536debcfc3c819e8"
+ integrity sha512-Vbsd/b+LYA99jUbsL6viEUWShFaYQt2YQs3QN3f+aeszOhh2sgdcU0mjzDyD4yyBvMc8qy2uwvBBWfMzEX06tg==
+ dependencies:
+ lodash "^4.17.21"
+ string-natural-compare "^3.0.1"
+
+eslint-plugin-jest@^26.5.3:
+ version "26.9.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.9.0.tgz#7931c31000b1c19e57dbfb71bbf71b817d1bf949"
+ integrity sha512-TWJxWGp1J628gxh2KhaH1H1paEdgE2J61BBF1I59c6xWeL5+D1BzMxGDN/nXAfX+aSkR5u80K+XhskK6Gwq9ng==
+ dependencies:
+ "@typescript-eslint/utils" "^5.10.0"
+
+eslint-plugin-prettier@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b"
+ integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==
+ dependencies:
+ prettier-linter-helpers "^1.0.0"
+
+eslint-plugin-react-hooks@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
+ integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
+
+eslint-plugin-react-native-globals@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz#ee1348bc2ceb912303ce6bdbd22e2f045ea86ea2"
+ integrity sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==
+
+eslint-plugin-react-native@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-native/-/eslint-plugin-react-native-4.1.0.tgz#5343acd3b2246bc1b857ac38be708f070d18809f"
+ integrity sha512-QLo7rzTBOl43FvVqDdq5Ql9IoElIuTdjrz9SKAXCvULvBoRZ44JGSkx9z4999ZusCsb4rK3gjS8gOGyeYqZv2Q==
+ dependencies:
+ eslint-plugin-react-native-globals "^0.1.1"
+
+eslint-plugin-react@^7.30.1:
+ version "7.34.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.34.0.tgz#ab71484d54fc409c37025c5eca00eb4177a5e88c"
+ integrity sha512-MeVXdReleBTdkz/bvcQMSnCXGi+c9kvy51IpinjnJgutl3YTHWsDdke7Z1ufZpGfDG8xduBDKyjtB9JH1eBKIQ==
+ dependencies:
+ array-includes "^3.1.7"
+ array.prototype.findlast "^1.2.4"
+ array.prototype.flatmap "^1.3.2"
+ array.prototype.toreversed "^1.1.2"
+ array.prototype.tosorted "^1.1.3"
+ doctrine "^2.1.0"
+ es-iterator-helpers "^1.0.17"
+ estraverse "^5.3.0"
+ jsx-ast-utils "^2.4.1 || ^3.0.0"
+ minimatch "^3.1.2"
+ object.entries "^1.1.7"
+ object.fromentries "^2.0.7"
+ object.hasown "^1.1.3"
+ object.values "^1.1.7"
+ prop-types "^15.8.1"
+ resolve "^2.0.0-next.5"
+ semver "^6.3.1"
+ string.prototype.matchall "^4.0.10"
+
+eslint-scope@5.1.1, eslint-scope@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+ integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^4.1.1"
+
+eslint-scope@^7.2.2:
+ version "7.2.2"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
+ integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^5.2.0"
+
+eslint-visitor-keys@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+ integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+
+eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
+ version "3.4.3"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
+ integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
+
+eslint@^8.19.0:
+ version "8.57.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668"
+ integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.2.0"
+ "@eslint-community/regexpp" "^4.6.1"
+ "@eslint/eslintrc" "^2.1.4"
+ "@eslint/js" "8.57.0"
+ "@humanwhocodes/config-array" "^0.11.14"
+ "@humanwhocodes/module-importer" "^1.0.1"
+ "@nodelib/fs.walk" "^1.2.8"
+ "@ungap/structured-clone" "^1.2.0"
+ ajv "^6.12.4"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.2"
+ debug "^4.3.2"
+ doctrine "^3.0.0"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^7.2.2"
+ eslint-visitor-keys "^3.4.3"
+ espree "^9.6.1"
+ esquery "^1.4.2"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^6.0.1"
+ find-up "^5.0.0"
+ glob-parent "^6.0.2"
+ globals "^13.19.0"
+ graphemer "^1.4.0"
+ ignore "^5.2.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ is-path-inside "^3.0.3"
+ js-yaml "^4.1.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.4.1"
+ lodash.merge "^4.6.2"
+ minimatch "^3.1.2"
+ natural-compare "^1.4.0"
+ optionator "^0.9.3"
+ strip-ansi "^6.0.1"
+ text-table "^0.2.0"
+
+espree@^9.6.0, espree@^9.6.1:
+ version "9.6.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
+ integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
+ dependencies:
+ acorn "^8.9.0"
+ acorn-jsx "^5.3.2"
+ eslint-visitor-keys "^3.4.1"
+
+esprima@^4.0.0, esprima@~4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+esquery@^1.4.2:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
+ integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
+ dependencies:
+ estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^4.1.1:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+etag@~1.8.1:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+ integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
+
+event-target-shim@^5.0.0, event-target-shim@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
+ integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
+
+execa@^5.0.0, execa@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
+ integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^6.0.0"
+ human-signals "^2.1.0"
+ is-stream "^2.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^4.0.1"
+ onetime "^5.1.2"
+ signal-exit "^3.0.3"
+ strip-final-newline "^2.0.0"
+
+exit@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
+ integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==
+
+expect@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc"
+ integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==
+ dependencies:
+ "@jest/expect-utils" "^29.7.0"
+ jest-get-type "^29.6.3"
+ jest-matcher-utils "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-diff@^1.1.2:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0"
+ integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
+
+fast-glob@^3.2.9, fast-glob@^3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
+ integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
+ dependencies:
+ "@nodelib/fs.stat" "^2.0.2"
+ "@nodelib/fs.walk" "^1.2.3"
+ glob-parent "^5.1.2"
+ merge2 "^1.3.0"
+ micromatch "^4.0.4"
+
+fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+
+fast-xml-parser@^4.0.12, fast-xml-parser@^4.2.4:
+ version "4.3.5"
+ resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.3.5.tgz#e2f2a2ae8377e9c3dc321b151e58f420ca7e5ccc"
+ integrity sha512-sWvP1Pl8H03B8oFJpFR3HE31HUfwtX7Rlf9BNsvdpujD4n7WMhfmu8h9wOV2u+c1k0ZilTADhPqypzx2J690ZQ==
+ dependencies:
+ strnum "^1.0.5"
+
+fastq@^1.6.0:
+ version "1.17.1"
+ resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47"
+ integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==
+ dependencies:
+ reusify "^1.0.4"
+
+fb-watchman@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c"
+ integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==
+ dependencies:
+ bser "2.1.1"
+
+file-entry-cache@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+ dependencies:
+ flat-cache "^3.0.4"
+
+fill-range@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+ integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+finalhandler@1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
+ integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
+ dependencies:
+ debug "2.6.9"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ on-finished "~2.3.0"
+ parseurl "~1.3.3"
+ statuses "~1.5.0"
+ unpipe "~1.0.0"
+
+find-cache-dir@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
+ integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==
+ dependencies:
+ commondir "^1.0.1"
+ make-dir "^2.0.0"
+ pkg-dir "^3.0.0"
+
+find-up@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+ integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+ dependencies:
+ locate-path "^3.0.0"
+
+find-up@^4.0.0, find-up@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+ integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+ dependencies:
+ locate-path "^5.0.0"
+ path-exists "^4.0.0"
+
+find-up@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+ integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+ dependencies:
+ locate-path "^6.0.0"
+ path-exists "^4.0.0"
+
+flat-cache@^3.0.4:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"
+ integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==
+ dependencies:
+ flatted "^3.2.9"
+ keyv "^4.5.3"
+ rimraf "^3.0.2"
+
+flatted@^3.2.9:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
+ integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
+
+flow-enums-runtime@^0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz#5bb0cd1b0a3e471330f4d109039b7eba5cb3e787"
+ integrity sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==
+
+flow-parser@0.*:
+ version "0.231.0"
+ resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.231.0.tgz#13daa172b3c06ffacbb31025592dc0db41fe28f3"
+ integrity sha512-WVzuqwq7ZnvBceCG0DGeTQebZE+iIU0mlk5PmJgYj9DDrt+0isGC2m1ezW9vxL4V+HERJJo9ExppOnwKH2op6Q==
+
+for-each@^0.3.3:
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
+ integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==
+ dependencies:
+ is-callable "^1.1.3"
+
+fresh@0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+ integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
+
+fs-extra@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+ integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
+ dependencies:
+ graceful-fs "^4.2.0"
+ jsonfile "^4.0.0"
+ universalify "^0.1.0"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+fsevents@^2.3.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+ integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
+function-bind@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+ integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
+function.prototype.name@^1.1.5, function.prototype.name@^1.1.6:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd"
+ integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+ functions-have-names "^1.2.3"
+
+functions-have-names@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
+ integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
+
+gensync@^1.0.0-beta.2:
+ version "1.0.0-beta.2"
+ resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
+ integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+
+get-caller-file@^2.0.1, get-caller-file@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
+ integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
+ dependencies:
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ has-proto "^1.0.1"
+ has-symbols "^1.0.3"
+ hasown "^2.0.0"
+
+get-package-type@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
+ integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
+
+get-stream@^6.0.0:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
+ integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
+
+get-symbol-description@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5"
+ integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==
+ dependencies:
+ call-bind "^1.0.5"
+ es-errors "^1.3.0"
+ get-intrinsic "^1.2.4"
+
+glob-parent@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+ dependencies:
+ is-glob "^4.0.1"
+
+glob-parent@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+ integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+ dependencies:
+ is-glob "^4.0.3"
+
+glob@^7.1.1, glob@^7.1.3, glob@^7.1.4:
+ version "7.2.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+ integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.1.1"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+globals@^11.1.0:
+ version "11.12.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+ integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+globals@^13.19.0:
+ version "13.24.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
+ integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==
+ dependencies:
+ type-fest "^0.20.2"
+
+globalthis@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf"
+ integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==
+ dependencies:
+ define-properties "^1.1.3"
+
+globby@^11.1.0:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+ integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
+ dependencies:
+ array-union "^2.1.0"
+ dir-glob "^3.0.1"
+ fast-glob "^3.2.9"
+ ignore "^5.2.0"
+ merge2 "^1.4.1"
+ slash "^3.0.0"
+
+gopd@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
+ integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
+ dependencies:
+ get-intrinsic "^1.1.3"
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
+ version "4.2.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+ integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
+graphemer@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
+ integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+
+has-bigints@^1.0.1, has-bigints@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
+ integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
+
+has-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
+ integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
+ dependencies:
+ es-define-property "^1.0.0"
+
+has-proto@^1.0.1, has-proto@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd"
+ integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==
+
+has-symbols@^1.0.2, has-symbols@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+ integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
+has-tostringtag@^1.0.0, has-tostringtag@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
+ integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
+ dependencies:
+ has-symbols "^1.0.3"
+
+hasown@^2.0.0, hasown@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
+ integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
+ dependencies:
+ function-bind "^1.1.2"
+
+hermes-estree@0.19.1:
+ version "0.19.1"
+ resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.19.1.tgz#d5924f5fac2bf0532547ae9f506d6db8f3c96392"
+ integrity sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g==
+
+hermes-parser@0.19.1:
+ version "0.19.1"
+ resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.19.1.tgz#1044348097165b7c93dc198a80b04ed5130d6b1a"
+ integrity sha512-Vp+bXzxYJWrpEuJ/vXxUsLnt0+y4q9zyi4zUlkLqD8FKv4LjIfOvP69R/9Lty3dCyKh0E2BU7Eypqr63/rKT/A==
+ dependencies:
+ hermes-estree "0.19.1"
+
+hermes-profile-transformer@^0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz#bd0f5ecceda80dd0ddaae443469ab26fb38fc27b"
+ integrity sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ==
+ dependencies:
+ source-map "^0.7.3"
+
+html-escaper@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
+ integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
+
+http-errors@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+ integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
+ dependencies:
+ depd "2.0.0"
+ inherits "2.0.4"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
+ toidentifier "1.0.1"
+
+human-signals@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
+ integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+
+ieee754@^1.1.13:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+ integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
+ignore@^5.0.5, ignore@^5.2.0, ignore@^5.2.4:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
+ integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
+
+image-size@^1.0.2:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.1.1.tgz#ddd67d4dc340e52ac29ce5f546a09f4e29e840ac"
+ integrity sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==
+ dependencies:
+ queue "6.0.2"
+
+import-fresh@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
+ integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==
+ dependencies:
+ caller-path "^2.0.0"
+ resolve-from "^3.0.0"
+
+import-fresh@^3.2.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
+import-local@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
+ integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==
+ dependencies:
+ pkg-dir "^4.2.0"
+ resolve-cwd "^3.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+internal-slot@^1.0.5, internal-slot@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802"
+ integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==
+ dependencies:
+ es-errors "^1.3.0"
+ hasown "^2.0.0"
+ side-channel "^1.0.4"
+
+invariant@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+ integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
+ dependencies:
+ loose-envify "^1.0.0"
+
+is-array-buffer@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98"
+ integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==
+ dependencies:
+ call-bind "^1.0.2"
+ get-intrinsic "^1.2.1"
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
+is-async-function@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646"
+ integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==
+ dependencies:
+ has-tostringtag "^1.0.0"
+
+is-bigint@^1.0.1:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
+ integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==
+ dependencies:
+ has-bigints "^1.0.1"
+
+is-boolean-object@^1.1.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
+ integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==
+ dependencies:
+ call-bind "^1.0.2"
+ has-tostringtag "^1.0.0"
+
+is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
+ integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
+
+is-core-module@^2.13.0:
+ version "2.13.1"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
+ integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
+ dependencies:
+ hasown "^2.0.0"
+
+is-date-object@^1.0.1, is-date-object@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
+ integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
+ dependencies:
+ has-tostringtag "^1.0.0"
+
+is-directory@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
+ integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==
+
+is-docker@^2.0.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
+ integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-finalizationregistry@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6"
+ integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==
+ dependencies:
+ call-bind "^1.0.2"
+
+is-fullwidth-code-point@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+ integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==
+
+is-fullwidth-code-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-generator-fn@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
+ integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
+
+is-generator-function@^1.0.10:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72"
+ integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==
+ dependencies:
+ has-tostringtag "^1.0.0"
+
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-interactive@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
+ integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
+
+is-map@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e"
+ integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
+
+is-negative-zero@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747"
+ integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==
+
+is-number-object@^1.0.4:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc"
+ integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==
+ dependencies:
+ has-tostringtag "^1.0.0"
+
+is-number@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-path-inside@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
+ integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
+
+is-plain-object@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+ integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+ dependencies:
+ isobject "^3.0.1"
+
+is-regex@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
+ integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
+ dependencies:
+ call-bind "^1.0.2"
+ has-tostringtag "^1.0.0"
+
+is-set@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d"
+ integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==
+
+is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688"
+ integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==
+ dependencies:
+ call-bind "^1.0.7"
+
+is-stream@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
+ integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
+
+is-string@^1.0.5, is-string@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
+ integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
+ dependencies:
+ has-tostringtag "^1.0.0"
+
+is-symbol@^1.0.2, is-symbol@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
+ integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
+ dependencies:
+ has-symbols "^1.0.2"
+
+is-typed-array@^1.1.13:
+ version "1.1.13"
+ resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229"
+ integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==
+ dependencies:
+ which-typed-array "^1.1.14"
+
+is-unicode-supported@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
+ integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
+
+is-weakmap@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd"
+ integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==
+
+is-weakref@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
+ integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
+ dependencies:
+ call-bind "^1.0.2"
+
+is-weakset@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007"
+ integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==
+ dependencies:
+ call-bind "^1.0.7"
+ get-intrinsic "^1.2.4"
+
+is-wsl@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
+ integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==
+
+is-wsl@^2.1.1, is-wsl@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
+ integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
+ dependencies:
+ is-docker "^2.0.0"
+
+isarray@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
+ integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
+
+isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+isobject@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+ integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
+
+istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756"
+ integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==
+
+istanbul-lib-instrument@^5.0.4:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d"
+ integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==
+ dependencies:
+ "@babel/core" "^7.12.3"
+ "@babel/parser" "^7.14.7"
+ "@istanbuljs/schema" "^0.1.2"
+ istanbul-lib-coverage "^3.2.0"
+ semver "^6.3.0"
+
+istanbul-lib-instrument@^6.0.0:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz#91655936cf7380e4e473383081e38478b69993b1"
+ integrity sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==
+ dependencies:
+ "@babel/core" "^7.23.9"
+ "@babel/parser" "^7.23.9"
+ "@istanbuljs/schema" "^0.1.3"
+ istanbul-lib-coverage "^3.2.0"
+ semver "^7.5.4"
+
+istanbul-lib-report@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d"
+ integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==
+ dependencies:
+ istanbul-lib-coverage "^3.0.0"
+ make-dir "^4.0.0"
+ supports-color "^7.1.0"
+
+istanbul-lib-source-maps@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551"
+ integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==
+ dependencies:
+ debug "^4.1.1"
+ istanbul-lib-coverage "^3.0.0"
+ source-map "^0.6.1"
+
+istanbul-reports@^3.1.3:
+ version "3.1.7"
+ resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b"
+ integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==
+ dependencies:
+ html-escaper "^2.0.0"
+ istanbul-lib-report "^3.0.0"
+
+iterator.prototype@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0"
+ integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==
+ dependencies:
+ define-properties "^1.2.1"
+ get-intrinsic "^1.2.1"
+ has-symbols "^1.0.3"
+ reflect.getprototypeof "^1.0.4"
+ set-function-name "^2.0.1"
+
+jest-changed-files@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a"
+ integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==
+ dependencies:
+ execa "^5.0.0"
+ jest-util "^29.7.0"
+ p-limit "^3.1.0"
+
+jest-circus@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a"
+ integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==
+ dependencies:
+ "@jest/environment" "^29.7.0"
+ "@jest/expect" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ co "^4.6.0"
+ dedent "^1.0.0"
+ is-generator-fn "^2.0.0"
+ jest-each "^29.7.0"
+ jest-matcher-utils "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-runtime "^29.7.0"
+ jest-snapshot "^29.7.0"
+ jest-util "^29.7.0"
+ p-limit "^3.1.0"
+ pretty-format "^29.7.0"
+ pure-rand "^6.0.0"
+ slash "^3.0.0"
+ stack-utils "^2.0.3"
+
+jest-cli@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995"
+ integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==
+ dependencies:
+ "@jest/core" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ chalk "^4.0.0"
+ create-jest "^29.7.0"
+ exit "^0.1.2"
+ import-local "^3.0.2"
+ jest-config "^29.7.0"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ yargs "^17.3.1"
+
+jest-config@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f"
+ integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==
+ dependencies:
+ "@babel/core" "^7.11.6"
+ "@jest/test-sequencer" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ babel-jest "^29.7.0"
+ chalk "^4.0.0"
+ ci-info "^3.2.0"
+ deepmerge "^4.2.2"
+ glob "^7.1.3"
+ graceful-fs "^4.2.9"
+ jest-circus "^29.7.0"
+ jest-environment-node "^29.7.0"
+ jest-get-type "^29.6.3"
+ jest-regex-util "^29.6.3"
+ jest-resolve "^29.7.0"
+ jest-runner "^29.7.0"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ micromatch "^4.0.4"
+ parse-json "^5.2.0"
+ pretty-format "^29.7.0"
+ slash "^3.0.0"
+ strip-json-comments "^3.1.1"
+
+jest-diff@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a"
+ integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==
+ dependencies:
+ chalk "^4.0.0"
+ diff-sequences "^29.6.3"
+ jest-get-type "^29.6.3"
+ pretty-format "^29.7.0"
+
+jest-docblock@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a"
+ integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==
+ dependencies:
+ detect-newline "^3.0.0"
+
+jest-each@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1"
+ integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ chalk "^4.0.0"
+ jest-get-type "^29.6.3"
+ jest-util "^29.7.0"
+ pretty-format "^29.7.0"
+
+jest-environment-node@^29.6.3, jest-environment-node@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376"
+ integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==
+ dependencies:
+ "@jest/environment" "^29.7.0"
+ "@jest/fake-timers" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ jest-mock "^29.7.0"
+ jest-util "^29.7.0"
+
+jest-get-type@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1"
+ integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==
+
+jest-haste-map@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104"
+ integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ "@types/graceful-fs" "^4.1.3"
+ "@types/node" "*"
+ anymatch "^3.0.3"
+ fb-watchman "^2.0.0"
+ graceful-fs "^4.2.9"
+ jest-regex-util "^29.6.3"
+ jest-util "^29.7.0"
+ jest-worker "^29.7.0"
+ micromatch "^4.0.4"
+ walker "^1.0.8"
+ optionalDependencies:
+ fsevents "^2.3.2"
+
+jest-leak-detector@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728"
+ integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==
+ dependencies:
+ jest-get-type "^29.6.3"
+ pretty-format "^29.7.0"
+
+jest-matcher-utils@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12"
+ integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==
+ dependencies:
+ chalk "^4.0.0"
+ jest-diff "^29.7.0"
+ jest-get-type "^29.6.3"
+ pretty-format "^29.7.0"
+
+jest-message-util@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3"
+ integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==
+ dependencies:
+ "@babel/code-frame" "^7.12.13"
+ "@jest/types" "^29.6.3"
+ "@types/stack-utils" "^2.0.0"
+ chalk "^4.0.0"
+ graceful-fs "^4.2.9"
+ micromatch "^4.0.4"
+ pretty-format "^29.7.0"
+ slash "^3.0.0"
+ stack-utils "^2.0.3"
+
+jest-mock@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347"
+ integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ jest-util "^29.7.0"
+
+jest-pnp-resolver@^1.2.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e"
+ integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==
+
+jest-regex-util@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52"
+ integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==
+
+jest-resolve-dependencies@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428"
+ integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==
+ dependencies:
+ jest-regex-util "^29.6.3"
+ jest-snapshot "^29.7.0"
+
+jest-resolve@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30"
+ integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==
+ dependencies:
+ chalk "^4.0.0"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
+ jest-pnp-resolver "^1.2.2"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ resolve "^1.20.0"
+ resolve.exports "^2.0.0"
+ slash "^3.0.0"
+
+jest-runner@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e"
+ integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==
+ dependencies:
+ "@jest/console" "^29.7.0"
+ "@jest/environment" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ emittery "^0.13.1"
+ graceful-fs "^4.2.9"
+ jest-docblock "^29.7.0"
+ jest-environment-node "^29.7.0"
+ jest-haste-map "^29.7.0"
+ jest-leak-detector "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-resolve "^29.7.0"
+ jest-runtime "^29.7.0"
+ jest-util "^29.7.0"
+ jest-watcher "^29.7.0"
+ jest-worker "^29.7.0"
+ p-limit "^3.1.0"
+ source-map-support "0.5.13"
+
+jest-runtime@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817"
+ integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==
+ dependencies:
+ "@jest/environment" "^29.7.0"
+ "@jest/fake-timers" "^29.7.0"
+ "@jest/globals" "^29.7.0"
+ "@jest/source-map" "^29.6.3"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ cjs-module-lexer "^1.0.0"
+ collect-v8-coverage "^1.0.0"
+ glob "^7.1.3"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-mock "^29.7.0"
+ jest-regex-util "^29.6.3"
+ jest-resolve "^29.7.0"
+ jest-snapshot "^29.7.0"
+ jest-util "^29.7.0"
+ slash "^3.0.0"
+ strip-bom "^4.0.0"
+
+jest-snapshot@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5"
+ integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==
+ dependencies:
+ "@babel/core" "^7.11.6"
+ "@babel/generator" "^7.7.2"
+ "@babel/plugin-syntax-jsx" "^7.7.2"
+ "@babel/plugin-syntax-typescript" "^7.7.2"
+ "@babel/types" "^7.3.3"
+ "@jest/expect-utils" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ babel-preset-current-node-syntax "^1.0.0"
+ chalk "^4.0.0"
+ expect "^29.7.0"
+ graceful-fs "^4.2.9"
+ jest-diff "^29.7.0"
+ jest-get-type "^29.6.3"
+ jest-matcher-utils "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
+ natural-compare "^1.4.0"
+ pretty-format "^29.7.0"
+ semver "^7.5.3"
+
+jest-util@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc"
+ integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ ci-info "^3.2.0"
+ graceful-fs "^4.2.9"
+ picomatch "^2.2.3"
+
+jest-validate@^29.6.3, jest-validate@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c"
+ integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ camelcase "^6.2.0"
+ chalk "^4.0.0"
+ jest-get-type "^29.6.3"
+ leven "^3.1.0"
+ pretty-format "^29.7.0"
+
+jest-watcher@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2"
+ integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==
+ dependencies:
+ "@jest/test-result" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ ansi-escapes "^4.2.1"
+ chalk "^4.0.0"
+ emittery "^0.13.1"
+ jest-util "^29.7.0"
+ string-length "^4.0.1"
+
+jest-worker@^29.6.3, jest-worker@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a"
+ integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==
+ dependencies:
+ "@types/node" "*"
+ jest-util "^29.7.0"
+ merge-stream "^2.0.0"
+ supports-color "^8.0.0"
+
+jest@^29.6.3:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613"
+ integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==
+ dependencies:
+ "@jest/core" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ import-local "^3.0.2"
+ jest-cli "^29.7.0"
+
+joi@^17.2.1:
+ version "17.12.2"
+ resolved "https://registry.yarnpkg.com/joi/-/joi-17.12.2.tgz#283a664dabb80c7e52943c557aab82faea09f521"
+ integrity sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw==
+ dependencies:
+ "@hapi/hoek" "^9.3.0"
+ "@hapi/topo" "^5.1.0"
+ "@sideway/address" "^4.1.5"
+ "@sideway/formula" "^3.0.1"
+ "@sideway/pinpoint" "^2.0.0"
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^3.13.1:
+ version "3.14.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
+ integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
+jsc-android@^250231.0.0:
+ version "250231.0.0"
+ resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-250231.0.0.tgz#91720f8df382a108872fa4b3f558f33ba5e95262"
+ integrity sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw==
+
+jsc-safe-url@^0.2.2:
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz#141c14fbb43791e88d5dc64e85a374575a83477a"
+ integrity sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==
+
+jscodeshift@^0.14.0:
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.14.0.tgz#7542e6715d6d2e8bde0b4e883f0ccea358b46881"
+ integrity sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA==
+ dependencies:
+ "@babel/core" "^7.13.16"
+ "@babel/parser" "^7.13.16"
+ "@babel/plugin-proposal-class-properties" "^7.13.0"
+ "@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8"
+ "@babel/plugin-proposal-optional-chaining" "^7.13.12"
+ "@babel/plugin-transform-modules-commonjs" "^7.13.8"
+ "@babel/preset-flow" "^7.13.13"
+ "@babel/preset-typescript" "^7.13.0"
+ "@babel/register" "^7.13.16"
+ babel-core "^7.0.0-bridge.0"
+ chalk "^4.1.2"
+ flow-parser "0.*"
+ graceful-fs "^4.2.4"
+ micromatch "^4.0.4"
+ neo-async "^2.5.0"
+ node-dir "^0.1.17"
+ recast "^0.21.0"
+ temp "^0.8.4"
+ write-file-atomic "^2.3.0"
+
+jsesc@^2.5.1:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+ integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+jsesc@~0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+ integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==
+
+json-buffer@3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
+ integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+
+json-parse-better-errors@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+ integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
+
+json-parse-even-better-errors@^2.3.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+ integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
+json5@^2.2.3:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+ integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
+jsonfile@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+ integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+"jsx-ast-utils@^2.4.1 || ^3.0.0":
+ version "3.3.5"
+ resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a"
+ integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==
+ dependencies:
+ array-includes "^3.1.6"
+ array.prototype.flat "^1.3.1"
+ object.assign "^4.1.4"
+ object.values "^1.1.6"
+
+keyv@^4.5.3:
+ version "4.5.4"
+ resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
+ integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
+ dependencies:
+ json-buffer "3.0.1"
+
+kind-of@^6.0.2:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+kleur@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
+ integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
+
+leven@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
+ integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
+
+levn@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ dependencies:
+ prelude-ls "^1.2.1"
+ type-check "~0.4.0"
+
+lighthouse-logger@^1.0.0:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz#aef90f9e97cd81db367c7634292ee22079280aaa"
+ integrity sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==
+ dependencies:
+ debug "^2.6.9"
+ marky "^1.2.2"
+
+lines-and-columns@^1.1.6:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+ integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
+locate-path@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+ integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+ dependencies:
+ p-locate "^3.0.0"
+ path-exists "^3.0.0"
+
+locate-path@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+ integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+ dependencies:
+ p-locate "^4.1.0"
+
+locate-path@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+ integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+ dependencies:
+ p-locate "^5.0.0"
+
+lodash.debounce@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
+ integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
+
+lodash.merge@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+lodash.throttle@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
+ integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==
+
+lodash@^4.17.21:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+log-symbols@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
+ integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
+ dependencies:
+ chalk "^4.1.0"
+ is-unicode-supported "^0.1.0"
+
+logkitty@^0.7.1:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/logkitty/-/logkitty-0.7.1.tgz#8e8d62f4085a826e8d38987722570234e33c6aa7"
+ integrity sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ==
+ dependencies:
+ ansi-fragments "^0.2.1"
+ dayjs "^1.8.15"
+ yargs "^15.1.0"
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+ dependencies:
+ js-tokens "^3.0.0 || ^4.0.0"
+
+lru-cache@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
+ integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
+ dependencies:
+ yallist "^3.0.2"
+
+lru-cache@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+ integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+ dependencies:
+ yallist "^4.0.0"
+
+make-dir@^2.0.0, make-dir@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
+ integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
+ dependencies:
+ pify "^4.0.1"
+ semver "^5.6.0"
+
+make-dir@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
+ integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==
+ dependencies:
+ semver "^7.5.3"
+
+makeerror@1.0.12:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
+ integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==
+ dependencies:
+ tmpl "1.0.5"
+
+marky@^1.2.2:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.5.tgz#55796b688cbd72390d2d399eaaf1832c9413e3c0"
+ integrity sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==
+
+memoize-one@^5.0.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
+ integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
+
+merge-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+ integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+merge2@^1.3.0, merge2@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+ integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
+metro-babel-transformer@0.80.6:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.80.6.tgz#49df74af71ecc9871636cf469726debcb5a1c858"
+ integrity sha512-ssuoVC4OzqaOt3LpwfUbDfBlFGRu9v1Yf2JJnKPz0ROYHNjSBws4aUesqQQ/Ea8DbiH7TK4j4cJmm+XjdHmgqA==
+ dependencies:
+ "@babel/core" "^7.20.0"
+ hermes-parser "0.19.1"
+ nullthrows "^1.1.1"
+
+metro-cache-key@0.80.6:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.80.6.tgz#48fe84477f6408478a33c363a8f5eaceea5cf853"
+ integrity sha512-DFmjQacC8m/S3HpELklLMWkPGP/fZPX3BSgjd0xQvwIvWyFwk8Nn/lfp/uWdEVDtDSIr64/anXU5uWohGwlWXw==
+
+metro-cache@0.80.6:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.80.6.tgz#05fdd83482f4132243b27713716c289532bd41c3"
+ integrity sha512-NP81pHSPkzs+iNlpVkJqijrpcd6lfuDAunYH9/Rn8oLNz0yLfkl8lt+xOdUU4IkFt3oVcTBEFCnzAzv4B8YhyA==
+ dependencies:
+ metro-core "0.80.6"
+ rimraf "^3.0.2"
+
+metro-config@0.80.6, metro-config@^0.80.3:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.80.6.tgz#b404e2f24b22c9c683abcf8da3efa8c87e382ad7"
+ integrity sha512-vHYYvJpRTWYbmvqlR7i04xQpZCHJ6yfZ/xIcPdz2ssbdJGGJbiT1Aar9wr8RAhsccSxdJgfE5B1DB8Mo+DnhIg==
+ dependencies:
+ connect "^3.6.5"
+ cosmiconfig "^5.0.5"
+ jest-validate "^29.6.3"
+ metro "0.80.6"
+ metro-cache "0.80.6"
+ metro-core "0.80.6"
+ metro-runtime "0.80.6"
+
+metro-core@0.80.6, metro-core@^0.80.3:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.80.6.tgz#b13fa98417e70203d2533c5d0f5c4d541f3d9fbe"
+ integrity sha512-fn4rryTUAwzFJWj7VIPDH4CcW/q7MV4oGobqR6NsuxZoIGYrVpK7pBasumu5YbCqifuErMs5s23BhmrDNeZURw==
+ dependencies:
+ lodash.throttle "^4.1.1"
+ metro-resolver "0.80.6"
+
+metro-file-map@0.80.6:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.80.6.tgz#9d96e54bd3bde6747b6860702a098a333599bba2"
+ integrity sha512-S3CUqvpXpc+q3q+hCEWvFKhVqgq0VmXdZQDF6u7ue86E2elq1XLnfLOt9JSpwyhpMQRyysjSCnd/Yh6GZMNHoQ==
+ dependencies:
+ anymatch "^3.0.3"
+ debug "^2.2.0"
+ fb-watchman "^2.0.0"
+ graceful-fs "^4.2.4"
+ invariant "^2.2.4"
+ jest-worker "^29.6.3"
+ micromatch "^4.0.4"
+ node-abort-controller "^3.1.1"
+ nullthrows "^1.1.1"
+ walker "^1.0.7"
+ optionalDependencies:
+ fsevents "^2.3.2"
+
+metro-minify-terser@0.80.6:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.80.6.tgz#27193867ec177c5a9b636725ff1c94c65ce701cc"
+ integrity sha512-83eZaH2+B+jP92KuodPqXknzwmiboKAuZY4doRfTEEXAG57pNVNN6cqSRJlwDnmaTBKRffxoncBXbYqHQgulgg==
+ dependencies:
+ terser "^5.15.0"
+
+metro-resolver@0.80.6:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.80.6.tgz#b648b8c661bc4cf091efd11affa010dd11f58bec"
+ integrity sha512-R7trfglG4zY4X9XyM9cvuffAhQ9W1reWoahr1jdEWa6rOI8PyM0qXjcsb8l+fsOQhdSiVlkKcYAmkyrs1S/zrA==
+
+metro-runtime@0.80.6, metro-runtime@^0.80.3:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.80.6.tgz#efd566a02e63e6f2bd08b5e2a8fe57333f1a2c4e"
+ integrity sha512-21GQVd0pp2nACoK0C2PL8mBsEhIFUFFntYrWRlYNHtPQoqDzddrPEIgkyaABGXGued+dZoBlFQl+LASlmmfkvw==
+ dependencies:
+ "@babel/runtime" "^7.0.0"
+
+metro-source-map@0.80.6, metro-source-map@^0.80.3:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.80.6.tgz#f129a36bb5b74e3ae0d4cbbcdc62904fa0161fb1"
+ integrity sha512-lqDuSLctWy9Qccu4Zl0YB1PzItpsqcKGb1nK0aDY+lzJ26X65OCib2VzHlj+xj7e4PiIKOfsvDCczCBz4cnxdg==
+ dependencies:
+ "@babel/traverse" "^7.20.0"
+ "@babel/types" "^7.20.0"
+ invariant "^2.2.4"
+ metro-symbolicate "0.80.6"
+ nullthrows "^1.1.1"
+ ob1 "0.80.6"
+ source-map "^0.5.6"
+ vlq "^1.0.0"
+
+metro-symbolicate@0.80.6:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.80.6.tgz#8690af051f33c98c0e8efcd779aebbfdea9fabef"
+ integrity sha512-SGwKeBi+lK7NmM5+EcW6DyRRa9HmGSvH0LJtlT4XoRMbpxzsLYs0qUEA+olD96pOIP+ta7I8S30nQr2ttqgO8A==
+ dependencies:
+ invariant "^2.2.4"
+ metro-source-map "0.80.6"
+ nullthrows "^1.1.1"
+ source-map "^0.5.6"
+ through2 "^2.0.1"
+ vlq "^1.0.0"
+
+metro-transform-plugins@0.80.6:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.80.6.tgz#f9039384692fc8cd51a67d1cd7c35964e7d374e8"
+ integrity sha512-e04tdTC5Fy1vOQrTTXb5biao0t7nR/h+b1IaBTlM5UaHaAJZr658uVOoZhkRxKjbhF2mIwJ/8DdorD2CA15BCg==
+ dependencies:
+ "@babel/core" "^7.20.0"
+ "@babel/generator" "^7.20.0"
+ "@babel/template" "^7.0.0"
+ "@babel/traverse" "^7.20.0"
+ nullthrows "^1.1.1"
+
+metro-transform-worker@0.80.6:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.80.6.tgz#fc09822ce360eaa929b14408e4af97a2fa8feba6"
+ integrity sha512-jV+VgCLiCj5jQadW/h09qJaqDreL6XcBRY52STCoz2xWn6WWLLMB5nXzQtvFNPmnIOps+Xu8+d5hiPcBNOhYmA==
+ dependencies:
+ "@babel/core" "^7.20.0"
+ "@babel/generator" "^7.20.0"
+ "@babel/parser" "^7.20.0"
+ "@babel/types" "^7.20.0"
+ metro "0.80.6"
+ metro-babel-transformer "0.80.6"
+ metro-cache "0.80.6"
+ metro-cache-key "0.80.6"
+ metro-minify-terser "0.80.6"
+ metro-source-map "0.80.6"
+ metro-transform-plugins "0.80.6"
+ nullthrows "^1.1.1"
+
+metro@0.80.6, metro@^0.80.3:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/metro/-/metro-0.80.6.tgz#11cf77700b8be767f6663c1d6f6ed287dd686535"
+ integrity sha512-f6Nhnht9TxVRP6zdBq9J2jNdeDBxRmJFnjxhQS1GeCpokBvI6fTXq+wHTLz5jZA+75fwbkPSzBxBJzQa6xi0AQ==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ "@babel/core" "^7.20.0"
+ "@babel/generator" "^7.20.0"
+ "@babel/parser" "^7.20.0"
+ "@babel/template" "^7.0.0"
+ "@babel/traverse" "^7.20.0"
+ "@babel/types" "^7.20.0"
+ accepts "^1.3.7"
+ chalk "^4.0.0"
+ ci-info "^2.0.0"
+ connect "^3.6.5"
+ debug "^2.2.0"
+ denodeify "^1.2.1"
+ error-stack-parser "^2.0.6"
+ graceful-fs "^4.2.4"
+ hermes-parser "0.19.1"
+ image-size "^1.0.2"
+ invariant "^2.2.4"
+ jest-worker "^29.6.3"
+ jsc-safe-url "^0.2.2"
+ lodash.throttle "^4.1.1"
+ metro-babel-transformer "0.80.6"
+ metro-cache "0.80.6"
+ metro-cache-key "0.80.6"
+ metro-config "0.80.6"
+ metro-core "0.80.6"
+ metro-file-map "0.80.6"
+ metro-resolver "0.80.6"
+ metro-runtime "0.80.6"
+ metro-source-map "0.80.6"
+ metro-symbolicate "0.80.6"
+ metro-transform-plugins "0.80.6"
+ metro-transform-worker "0.80.6"
+ mime-types "^2.1.27"
+ node-fetch "^2.2.0"
+ nullthrows "^1.1.1"
+ rimraf "^3.0.2"
+ serialize-error "^2.1.0"
+ source-map "^0.5.6"
+ strip-ansi "^6.0.0"
+ throat "^5.0.0"
+ ws "^7.5.1"
+ yargs "^17.6.2"
+
+micromatch@^4.0.4:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+ integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ dependencies:
+ braces "^3.0.2"
+ picomatch "^2.3.1"
+
+mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.27, mime-types@~2.1.34:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+mime@1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+ integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+
+mime@^2.4.1:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
+ integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
+
+mimic-fn@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+ integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+minimatch@9.0.3:
+ version "9.0.3"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
+ integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
+ dependencies:
+ brace-expansion "^2.0.1"
+
+minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@^1.2.6:
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+ integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+
+mkdirp@^0.5.1:
+ version "0.5.6"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
+ integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
+ dependencies:
+ minimist "^1.2.6"
+
+mkdirp@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
+ integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
+
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+ms@2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+
+negotiator@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
+ integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
+
+neo-async@^2.5.0:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+ integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+
+nocache@^3.0.1:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/nocache/-/nocache-3.0.4.tgz#5b37a56ec6e09fc7d401dceaed2eab40c8bfdf79"
+ integrity sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==
+
+node-abort-controller@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548"
+ integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==
+
+node-dir@^0.1.17:
+ version "0.1.17"
+ resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5"
+ integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==
+ dependencies:
+ minimatch "^3.0.2"
+
+node-fetch@^2.2.0, node-fetch@^2.6.0:
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
+ integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
+ dependencies:
+ whatwg-url "^5.0.0"
+
+node-forge@^1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
+ integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
+
+node-int64@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
+ integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
+
+node-releases@^2.0.14:
+ version "2.0.14"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b"
+ integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==
+
+node-stream-zip@^1.9.1:
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea"
+ integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==
+
+normalize-path@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+ integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+npm-run-path@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
+ integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
+ dependencies:
+ path-key "^3.0.0"
+
+nullthrows@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
+ integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==
+
+ob1@0.80.6:
+ version "0.80.6"
+ resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.80.6.tgz#61d7881f458333ed2a73b90cea4aa62f8ca9e045"
+ integrity sha512-nlLGZPMQ/kbmkdIb5yvVzep1jKUII2x6ehNsHpgy71jpnJMW7V+KsB3AjYI2Ajb7UqMAMNjlssg6FUodrEMYzg==
+
+object-assign@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
+
+object-inspect@^1.13.1:
+ version "1.13.1"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
+ integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
+
+object-keys@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+ integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object.assign@^4.1.4, object.assign@^4.1.5:
+ version "4.1.5"
+ resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0"
+ integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==
+ dependencies:
+ call-bind "^1.0.5"
+ define-properties "^1.2.1"
+ has-symbols "^1.0.3"
+ object-keys "^1.1.1"
+
+object.entries@^1.1.7:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.7.tgz#2b47760e2a2e3a752f39dd874655c61a7f03c131"
+ integrity sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+
+object.fromentries@^2.0.7:
+ version "2.0.7"
+ resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616"
+ integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+
+object.hasown@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.3.tgz#6a5f2897bb4d3668b8e79364f98ccf971bda55ae"
+ integrity sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==
+ dependencies:
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+
+object.values@^1.1.6, object.values@^1.1.7:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a"
+ integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+
+on-finished@2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
+ integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
+ dependencies:
+ ee-first "1.1.1"
+
+on-finished@~2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+ integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==
+ dependencies:
+ ee-first "1.1.1"
+
+on-headers@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
+ integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ dependencies:
+ wrappy "1"
+
+onetime@^5.1.0, onetime@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+ integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+ dependencies:
+ mimic-fn "^2.1.0"
+
+open@^6.2.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9"
+ integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==
+ dependencies:
+ is-wsl "^1.1.0"
+
+open@^7.0.3:
+ version "7.4.2"
+ resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321"
+ integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==
+ dependencies:
+ is-docker "^2.0.0"
+ is-wsl "^2.1.1"
+
+optionator@^0.9.3:
+ version "0.9.3"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64"
+ integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==
+ dependencies:
+ "@aashutoshrathi/word-wrap" "^1.2.3"
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+
+ora@^5.4.1:
+ version "5.4.1"
+ resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18"
+ integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==
+ dependencies:
+ bl "^4.1.0"
+ chalk "^4.1.0"
+ cli-cursor "^3.1.0"
+ cli-spinners "^2.5.0"
+ is-interactive "^1.0.0"
+ is-unicode-supported "^0.1.0"
+ log-symbols "^4.1.0"
+ strip-ansi "^6.0.0"
+ wcwidth "^1.0.1"
+
+p-limit@^2.0.0, p-limit@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+ integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+ dependencies:
+ p-try "^2.0.0"
+
+p-limit@^3.0.2, p-limit@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+ integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+ dependencies:
+ yocto-queue "^0.1.0"
+
+p-locate@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+ integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+ dependencies:
+ p-limit "^2.0.0"
+
+p-locate@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+ integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+ dependencies:
+ p-limit "^2.2.0"
+
+p-locate@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+ integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+ dependencies:
+ p-limit "^3.0.2"
+
+p-try@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
+parse-json@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+ integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==
+ dependencies:
+ error-ex "^1.3.1"
+ json-parse-better-errors "^1.0.1"
+
+parse-json@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+ integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ error-ex "^1.3.1"
+ json-parse-even-better-errors "^2.3.0"
+ lines-and-columns "^1.1.6"
+
+parseurl@~1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+ integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+path-exists@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+ integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==
+
+path-exists@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+
+path-key@^3.0.0, path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-parse@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+path-type@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+ integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+pify@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+ integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+
+pirates@^4.0.4, pirates@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9"
+ integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
+
+pkg-dir@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
+ integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==
+ dependencies:
+ find-up "^3.0.0"
+
+pkg-dir@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
+ integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
+ dependencies:
+ find-up "^4.0.0"
+
+possible-typed-array-names@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f"
+ integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==
+
+prelude-ls@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+prettier-linter-helpers@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+ integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
+ dependencies:
+ fast-diff "^1.1.2"
+
+prettier@2.8.8:
+ version "2.8.8"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
+ integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
+
+pretty-format@^26.5.2, pretty-format@^26.6.2:
+ version "26.6.2"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
+ integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
+ dependencies:
+ "@jest/types" "^26.6.2"
+ ansi-regex "^5.0.0"
+ ansi-styles "^4.0.0"
+ react-is "^17.0.1"
+
+pretty-format@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
+ integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
+ dependencies:
+ "@jest/schemas" "^29.6.3"
+ ansi-styles "^5.0.0"
+ react-is "^18.0.0"
+
+process-nextick-args@~2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+ integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+promise@^8.3.0:
+ version "8.3.0"
+ resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a"
+ integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==
+ dependencies:
+ asap "~2.0.6"
+
+prompts@^2.0.1, prompts@^2.4.2:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
+ integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==
+ dependencies:
+ kleur "^3.0.3"
+ sisteransi "^1.0.5"
+
+prop-types@^15.8.1:
+ version "15.8.1"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
+ integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.13.1"
+
+punycode@^2.1.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+ integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
+pure-rand@^6.0.0:
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7"
+ integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==
+
+querystring@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd"
+ integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==
+
+queue-microtask@^1.2.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+ integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
+queue@6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65"
+ integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==
+ dependencies:
+ inherits "~2.0.3"
+
+range-parser@~1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+ integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+react-devtools-core@^5.0.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-5.2.0.tgz#072ecd2d84d3653817cc11e4b16f60a3c2b705f9"
+ integrity sha512-vZK+/gvxxsieAoAyYaiRIVFxlajb7KXhgBDV7OsoMzaAE+IqGpoxusBjIgq5ibqA2IloKu0p9n7tE68z1xs18A==
+ dependencies:
+ shell-quote "^1.6.1"
+ ws "^7"
+
+"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
+ integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
+
+react-is@^16.13.1:
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+ integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
+react-is@^17.0.1:
+ version "17.0.2"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
+ integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+
+react-native@0.74.0:
+ version "0.74.0"
+ resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.74.0.tgz#9f0901139424152216e1ae1b32773787a0158d41"
+ integrity sha512-Vpp9WPmkCm4TUH5YDxwQhqktGVon/yLpjbTgjgLqup3GglOgWagYCX3MlmK1iksIcqtyMJHMEWa+UEzJ3G9T8w==
+ dependencies:
+ "@jest/create-cache-key-function" "^29.6.3"
+ "@react-native-community/cli" "13.6.4"
+ "@react-native-community/cli-platform-android" "13.6.4"
+ "@react-native-community/cli-platform-ios" "13.6.4"
+ "@react-native/assets-registry" "0.74.81"
+ "@react-native/codegen" "0.74.81"
+ "@react-native/community-cli-plugin" "0.74.81"
+ "@react-native/gradle-plugin" "0.74.81"
+ "@react-native/js-polyfills" "0.74.81"
+ "@react-native/normalize-colors" "0.74.81"
+ "@react-native/virtualized-lists" "0.74.81"
+ abort-controller "^3.0.0"
+ anser "^1.4.9"
+ ansi-regex "^5.0.0"
+ base64-js "^1.5.1"
+ chalk "^4.0.0"
+ event-target-shim "^5.0.1"
+ flow-enums-runtime "^0.0.6"
+ invariant "^2.2.4"
+ jest-environment-node "^29.6.3"
+ jsc-android "^250231.0.0"
+ memoize-one "^5.0.0"
+ metro-runtime "^0.80.3"
+ metro-source-map "^0.80.3"
+ mkdirp "^0.5.1"
+ nullthrows "^1.1.1"
+ pretty-format "^26.5.2"
+ promise "^8.3.0"
+ react-devtools-core "^5.0.0"
+ react-refresh "^0.14.0"
+ react-shallow-renderer "^16.15.0"
+ regenerator-runtime "^0.13.2"
+ scheduler "0.24.0-canary-efb381bbf-20230505"
+ stacktrace-parser "^0.1.10"
+ whatwg-fetch "^3.0.0"
+ ws "^6.2.2"
+ yargs "^17.6.2"
+
+react-refresh@^0.14.0:
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
+ integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
+
+react-shallow-renderer@^16.15.0:
+ version "16.15.0"
+ resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457"
+ integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==
+ dependencies:
+ object-assign "^4.1.1"
+ react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
+
+react-test-renderer@18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e"
+ integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==
+ dependencies:
+ react-is "^18.2.0"
+ react-shallow-renderer "^16.15.0"
+ scheduler "^0.23.0"
+
+react@18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
+ integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
+ dependencies:
+ loose-envify "^1.1.0"
+
+readable-stream@^3.4.0:
+ version "3.6.2"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
+ integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
+ dependencies:
+ inherits "^2.0.3"
+ string_decoder "^1.1.1"
+ util-deprecate "^1.0.1"
+
+readable-stream@~2.3.6:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
+ integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.3"
+ isarray "~1.0.0"
+ process-nextick-args "~2.0.0"
+ safe-buffer "~5.1.1"
+ string_decoder "~1.1.1"
+ util-deprecate "~1.0.1"
+
+readline@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c"
+ integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==
+
+recast@^0.21.0:
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/recast/-/recast-0.21.5.tgz#e8cd22bb51bcd6130e54f87955d33a2b2e57b495"
+ integrity sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==
+ dependencies:
+ ast-types "0.15.2"
+ esprima "~4.0.0"
+ source-map "~0.6.1"
+ tslib "^2.0.1"
+
+reflect.getprototypeof@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz#e0bd28b597518f16edaf9c0e292c631eb13e0674"
+ integrity sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==
+ dependencies:
+ call-bind "^1.0.5"
+ define-properties "^1.2.1"
+ es-abstract "^1.22.3"
+ es-errors "^1.0.0"
+ get-intrinsic "^1.2.3"
+ globalthis "^1.0.3"
+ which-builtin-type "^1.1.3"
+
+regenerate-unicode-properties@^10.1.0:
+ version "10.1.1"
+ resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480"
+ integrity sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==
+ dependencies:
+ regenerate "^1.4.2"
+
+regenerate@^1.4.2:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
+ integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
+
+regenerator-runtime@^0.13.2:
+ version "0.13.11"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
+ integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
+
+regenerator-runtime@^0.14.0:
+ version "0.14.1"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
+ integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
+
+regenerator-transform@^0.15.2:
+ version "0.15.2"
+ resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4"
+ integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==
+ dependencies:
+ "@babel/runtime" "^7.8.4"
+
+regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.2:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334"
+ integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==
+ dependencies:
+ call-bind "^1.0.6"
+ define-properties "^1.2.1"
+ es-errors "^1.3.0"
+ set-function-name "^2.0.1"
+
+regexpu-core@^5.3.1:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b"
+ integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==
+ dependencies:
+ "@babel/regjsgen" "^0.8.0"
+ regenerate "^1.4.2"
+ regenerate-unicode-properties "^10.1.0"
+ regjsparser "^0.9.1"
+ unicode-match-property-ecmascript "^2.0.0"
+ unicode-match-property-value-ecmascript "^2.1.0"
+
+regjsparser@^0.9.1:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709"
+ integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==
+ dependencies:
+ jsesc "~0.5.0"
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+
+require-main-filename@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+ integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+
+resolve-cwd@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
+ integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
+ dependencies:
+ resolve-from "^5.0.0"
+
+resolve-from@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+ integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==
+
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve-from@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
+ integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+
+resolve.exports@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800"
+ integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
+
+resolve@^1.14.2, resolve@^1.20.0:
+ version "1.22.8"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
+ integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
+ dependencies:
+ is-core-module "^2.13.0"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+resolve@^2.0.0-next.5:
+ version "2.0.0-next.5"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c"
+ integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==
+ dependencies:
+ is-core-module "^2.13.0"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+restore-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+ integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+ dependencies:
+ onetime "^5.1.0"
+ signal-exit "^3.0.2"
+
+reusify@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+ integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+rimraf@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+ dependencies:
+ glob "^7.1.3"
+
+rimraf@~2.6.2:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+ integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+ dependencies:
+ glob "^7.1.3"
+
+run-parallel@^1.1.9:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+ integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+ dependencies:
+ queue-microtask "^1.2.2"
+
+safe-array-concat@^1.1.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb"
+ integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==
+ dependencies:
+ call-bind "^1.0.7"
+ get-intrinsic "^1.2.4"
+ has-symbols "^1.0.3"
+ isarray "^2.0.5"
+
+safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-buffer@~5.2.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+safe-regex-test@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377"
+ integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==
+ dependencies:
+ call-bind "^1.0.6"
+ es-errors "^1.3.0"
+ is-regex "^1.1.4"
+
+scheduler@0.24.0-canary-efb381bbf-20230505:
+ version "0.24.0-canary-efb381bbf-20230505"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.24.0-canary-efb381bbf-20230505.tgz#5dddc60e29f91cd7f8b983d7ce4a99c2202d178f"
+ integrity sha512-ABvovCDe/k9IluqSh4/ISoq8tIJnW8euVAWYt5j/bg6dRnqwQwiGO1F/V4AyK96NGF/FB04FhOUDuWj8IKfABA==
+ dependencies:
+ loose-envify "^1.1.0"
+
+scheduler@^0.23.0:
+ version "0.23.0"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
+ integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
+ dependencies:
+ loose-envify "^1.1.0"
+
+selfsigned@^2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0"
+ integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==
+ dependencies:
+ "@types/node-forge" "^1.3.0"
+ node-forge "^1"
+
+semver@^5.6.0:
+ version "5.7.2"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
+ integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
+
+semver@^6.3.0, semver@^6.3.1:
+ version "6.3.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
+ integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+
+semver@^7.3.7, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4:
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
+ integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
+ dependencies:
+ lru-cache "^6.0.0"
+
+send@0.18.0:
+ version "0.18.0"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
+ integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
+ dependencies:
+ debug "2.6.9"
+ depd "2.0.0"
+ destroy "1.2.0"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ etag "~1.8.1"
+ fresh "0.5.2"
+ http-errors "2.0.0"
+ mime "1.6.0"
+ ms "2.1.3"
+ on-finished "2.4.1"
+ range-parser "~1.2.1"
+ statuses "2.0.1"
+
+serialize-error@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a"
+ integrity sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==
+
+serve-static@^1.13.1:
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
+ integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
+ dependencies:
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ parseurl "~1.3.3"
+ send "0.18.0"
+
+set-blocking@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+ integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
+
+set-function-length@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
+ integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
+ dependencies:
+ define-data-property "^1.1.4"
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ get-intrinsic "^1.2.4"
+ gopd "^1.0.1"
+ has-property-descriptors "^1.0.2"
+
+set-function-name@^2.0.0, set-function-name@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985"
+ integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==
+ dependencies:
+ define-data-property "^1.1.4"
+ es-errors "^1.3.0"
+ functions-have-names "^1.2.3"
+ has-property-descriptors "^1.0.2"
+
+setprototypeof@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+ integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+
+shallow-clone@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
+ integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
+ dependencies:
+ kind-of "^6.0.2"
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+shell-quote@^1.6.1, shell-quote@^1.7.3:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680"
+ integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==
+
+side-channel@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
+ integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
+ dependencies:
+ call-bind "^1.0.7"
+ es-errors "^1.3.0"
+ get-intrinsic "^1.2.4"
+ object-inspect "^1.13.1"
+
+signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+ integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+sisteransi@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
+ integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
+
+slash@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+ integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
+slice-ansi@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
+ integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==
+ dependencies:
+ ansi-styles "^3.2.0"
+ astral-regex "^1.0.0"
+ is-fullwidth-code-point "^2.0.0"
+
+source-map-support@0.5.13:
+ version "0.5.13"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
+ integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map-support@^0.5.16, source-map-support@~0.5.20:
+ version "0.5.21"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+ integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map@^0.5.6:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+ integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
+
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+source-map@^0.7.3:
+ version "0.7.4"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
+ integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
+
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
+
+stack-utils@^2.0.3:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f"
+ integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==
+ dependencies:
+ escape-string-regexp "^2.0.0"
+
+stackframe@^1.3.4:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
+ integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
+
+stacktrace-parser@^0.1.10:
+ version "0.1.10"
+ resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a"
+ integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==
+ dependencies:
+ type-fest "^0.7.1"
+
+statuses@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+ integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+
+statuses@~1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+ integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
+
+string-length@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
+ integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==
+ dependencies:
+ char-regex "^1.0.2"
+ strip-ansi "^6.0.0"
+
+string-natural-compare@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
+ integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
+
+string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string.prototype.matchall@^4.0.10:
+ version "4.0.10"
+ resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100"
+ integrity sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+ get-intrinsic "^1.2.1"
+ has-symbols "^1.0.3"
+ internal-slot "^1.0.5"
+ regexp.prototype.flags "^1.5.0"
+ set-function-name "^2.0.0"
+ side-channel "^1.0.4"
+
+string.prototype.trim@^1.2.8:
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd"
+ integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+
+string.prototype.trimend@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e"
+ integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+
+string.prototype.trimstart@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298"
+ integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.2.0"
+ es-abstract "^1.22.1"
+
+string_decoder@^1.1.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+ integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+ dependencies:
+ safe-buffer "~5.2.0"
+
+string_decoder@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+ integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+ dependencies:
+ safe-buffer "~5.1.0"
+
+strip-ansi@^5.0.0, strip-ansi@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
+ integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
+ dependencies:
+ ansi-regex "^4.1.0"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-bom@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
+ integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==
+
+strip-final-newline@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
+ integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
+
+strip-json-comments@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+strnum@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db"
+ integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==
+
+sudo-prompt@^9.0.0:
+ version "9.2.1"
+ resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd"
+ integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==
+
+supports-color@^5.3.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+ dependencies:
+ has-flag "^3.0.0"
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-color@^8.0.0:
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+temp-dir@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e"
+ integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==
+
+temp@^0.8.4:
+ version "0.8.4"
+ resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2"
+ integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==
+ dependencies:
+ rimraf "~2.6.2"
+
+terser@^5.15.0:
+ version "5.29.2"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.29.2.tgz#c17d573ce1da1b30f21a877bffd5655dd86fdb35"
+ integrity sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==
+ dependencies:
+ "@jridgewell/source-map" "^0.3.3"
+ acorn "^8.8.2"
+ commander "^2.20.0"
+ source-map-support "~0.5.20"
+
+test-exclude@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
+ integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==
+ dependencies:
+ "@istanbuljs/schema" "^0.1.2"
+ glob "^7.1.4"
+ minimatch "^3.0.4"
+
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
+
+throat@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b"
+ integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==
+
+through2@^2.0.1:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
+ integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
+ dependencies:
+ readable-stream "~2.3.6"
+ xtend "~4.0.1"
+
+tmpl@1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
+ integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
+
+to-fast-properties@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+ integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
+
+to-regex-range@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ dependencies:
+ is-number "^7.0.0"
+
+toidentifier@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+ integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
+tr46@~0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+ integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
+
+ts-api-utils@^1.0.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
+ integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
+
+tslib@^1.8.1:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+ integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+
+tslib@^2.0.1:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
+ integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
+
+tsutils@^3.21.0:
+ version "3.21.0"
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
+ integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
+ dependencies:
+ tslib "^1.8.1"
+
+type-check@^0.4.0, type-check@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ dependencies:
+ prelude-ls "^1.2.1"
+
+type-detect@4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
+ integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
+
+type-fest@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+type-fest@^0.21.3:
+ version "0.21.3"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
+ integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
+
+type-fest@^0.7.1:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"
+ integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==
+
+typed-array-buffer@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3"
+ integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==
+ dependencies:
+ call-bind "^1.0.7"
+ es-errors "^1.3.0"
+ is-typed-array "^1.1.13"
+
+typed-array-byte-length@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67"
+ integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==
+ dependencies:
+ call-bind "^1.0.7"
+ for-each "^0.3.3"
+ gopd "^1.0.1"
+ has-proto "^1.0.3"
+ is-typed-array "^1.1.13"
+
+typed-array-byte-offset@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063"
+ integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==
+ dependencies:
+ available-typed-arrays "^1.0.7"
+ call-bind "^1.0.7"
+ for-each "^0.3.3"
+ gopd "^1.0.1"
+ has-proto "^1.0.3"
+ is-typed-array "^1.1.13"
+
+typed-array-length@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.5.tgz#57d44da160296d8663fd63180a1802ebf25905d5"
+ integrity sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==
+ dependencies:
+ call-bind "^1.0.7"
+ for-each "^0.3.3"
+ gopd "^1.0.1"
+ has-proto "^1.0.3"
+ is-typed-array "^1.1.13"
+ possible-typed-array-names "^1.0.0"
+
+typescript@5.0.4:
+ version "5.0.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
+ integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
+
+unbox-primitive@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
+ integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==
+ dependencies:
+ call-bind "^1.0.2"
+ has-bigints "^1.0.2"
+ has-symbols "^1.0.3"
+ which-boxed-primitive "^1.0.2"
+
+undici-types@~5.26.4:
+ version "5.26.5"
+ resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
+ integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
+
+unicode-canonical-property-names-ecmascript@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
+ integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==
+
+unicode-match-property-ecmascript@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3"
+ integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==
+ dependencies:
+ unicode-canonical-property-names-ecmascript "^2.0.0"
+ unicode-property-aliases-ecmascript "^2.0.0"
+
+unicode-match-property-value-ecmascript@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0"
+ integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==
+
+unicode-property-aliases-ecmascript@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd"
+ integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==
+
+universalify@^0.1.0:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+ integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
+unpipe@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+ integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
+
+update-browserslist-db@^1.0.13:
+ version "1.0.13"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4"
+ integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==
+ dependencies:
+ escalade "^3.1.1"
+ picocolors "^1.0.0"
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+util-deprecate@^1.0.1, util-deprecate@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+
+utils-merge@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+ integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
+
+v8-to-istanbul@^9.0.1:
+ version "9.2.0"
+ resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad"
+ integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==
+ dependencies:
+ "@jridgewell/trace-mapping" "^0.3.12"
+ "@types/istanbul-lib-coverage" "^2.0.1"
+ convert-source-map "^2.0.0"
+
+vary@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+ integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
+
+vlq@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468"
+ integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==
+
+walker@^1.0.7, walker@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
+ integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==
+ dependencies:
+ makeerror "1.0.12"
+
+wcwidth@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
+ integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==
+ dependencies:
+ defaults "^1.0.3"
+
+webidl-conversions@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+ integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
+
+whatwg-fetch@^3.0.0:
+ version "3.6.20"
+ resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70"
+ integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==
+
+whatwg-url@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
+ integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
+ dependencies:
+ tr46 "~0.0.3"
+ webidl-conversions "^3.0.0"
+
+which-boxed-primitive@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
+ integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
+ dependencies:
+ is-bigint "^1.0.1"
+ is-boolean-object "^1.1.0"
+ is-number-object "^1.0.4"
+ is-string "^1.0.5"
+ is-symbol "^1.0.3"
+
+which-builtin-type@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b"
+ integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==
+ dependencies:
+ function.prototype.name "^1.1.5"
+ has-tostringtag "^1.0.0"
+ is-async-function "^2.0.0"
+ is-date-object "^1.0.5"
+ is-finalizationregistry "^1.0.2"
+ is-generator-function "^1.0.10"
+ is-regex "^1.1.4"
+ is-weakref "^1.0.2"
+ isarray "^2.0.5"
+ which-boxed-primitive "^1.0.2"
+ which-collection "^1.0.1"
+ which-typed-array "^1.1.9"
+
+which-collection@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0"
+ integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==
+ dependencies:
+ is-map "^2.0.3"
+ is-set "^2.0.3"
+ is-weakmap "^2.0.2"
+ is-weakset "^2.0.3"
+
+which-module@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409"
+ integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==
+
+which-typed-array@^1.1.14, which-typed-array@^1.1.9:
+ version "1.1.15"
+ resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d"
+ integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==
+ dependencies:
+ available-typed-arrays "^1.0.7"
+ call-bind "^1.0.7"
+ for-each "^0.3.3"
+ gopd "^1.0.1"
+ has-tostringtag "^1.0.2"
+
+which@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+wrap-ansi@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+ integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+write-file-atomic@^2.3.0:
+ version "2.4.3"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481"
+ integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==
+ dependencies:
+ graceful-fs "^4.1.11"
+ imurmurhash "^0.1.4"
+ signal-exit "^3.0.2"
+
+write-file-atomic@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd"
+ integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==
+ dependencies:
+ imurmurhash "^0.1.4"
+ signal-exit "^3.0.7"
+
+ws@^6.2.2:
+ version "6.2.2"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e"
+ integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==
+ dependencies:
+ async-limiter "~1.0.0"
+
+ws@^7, ws@^7.5.1:
+ version "7.5.9"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
+ integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
+
+xtend@~4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+ integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+
+y18n@^4.0.0:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
+ integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
+
+y18n@^5.0.5:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+ integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
+yallist@^3.0.2:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+ integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+
+yallist@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+ integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+yaml@^2.2.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.1.tgz#2e57e0b5e995292c25c75d2658f0664765210eed"
+ integrity sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==
+
+yargs-parser@^18.1.2:
+ version "18.1.3"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
+ integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
+ dependencies:
+ camelcase "^5.0.0"
+ decamelize "^1.2.0"
+
+yargs-parser@^21.1.1:
+ version "21.1.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
+ integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
+
+yargs@^15.1.0:
+ version "15.4.1"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
+ integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
+ dependencies:
+ cliui "^6.0.0"
+ decamelize "^1.2.0"
+ find-up "^4.1.0"
+ get-caller-file "^2.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^2.0.0"
+ set-blocking "^2.0.0"
+ string-width "^4.2.0"
+ which-module "^2.0.0"
+ y18n "^4.0.0"
+ yargs-parser "^18.1.2"
+
+yargs@^17.3.1, yargs@^17.6.2:
+ version "17.7.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
+ integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
+ dependencies:
+ cliui "^8.0.1"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
+ require-directory "^2.1.1"
+ string-width "^4.2.3"
+ y18n "^5.0.5"
+ yargs-parser "^21.1.1"
+
+yocto-queue@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+ integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
diff --git a/third-party/nwaku/examples/nim.cfg b/third-party/nwaku/examples/nim.cfg
new file mode 100644
index 0000000..2f8d454
--- /dev/null
+++ b/third-party/nwaku/examples/nim.cfg
@@ -0,0 +1,5 @@
+-d:chronicles_line_numbers
+-d:chronicles_log_level="DEBUG"
+-d:chronicles_runtime_filtering=on
+-d:discv5_protocol_id="d5waku"
+path = "../"
diff --git a/third-party/nwaku/examples/nodejs/binding.gyp b/third-party/nwaku/examples/nodejs/binding.gyp
new file mode 100644
index 0000000..38a1656
--- /dev/null
+++ b/third-party/nwaku/examples/nodejs/binding.gyp
@@ -0,0 +1,9 @@
+{
+ "targets": [
+ {
+ "target_name": "waku",
+ "sources": [ "waku_addon.c", "../cbindings/base64.c" ],
+ "libraries": [ "-lwaku", "-L../../../build/" ]
+ }
+ ]
+}
diff --git a/third-party/nwaku/examples/nodejs/waku.js b/third-party/nwaku/examples/nodejs/waku.js
new file mode 100644
index 0000000..258e348
--- /dev/null
+++ b/third-party/nwaku/examples/nodejs/waku.js
@@ -0,0 +1,78 @@
+
+var express = require('express');
+var app = express();
+
+function create_random_string(length) {
+ let result = '';
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+ const charactersLength = characters.length;
+ let counter = 0;
+ while (counter < length) {
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
+ counter += 1;
+ }
+ return result;
+}
+
+var wakuMod = require('bindings')('waku');
+
+var cfg = `{
+ "host": "0.0.0.0",
+ "port": 60001,
+ "key": "364d111d729a6eb6d3e6113e163f017b5ef03a6f94c9b5b7bb1bb36fa5cb07a9",
+ "relay": true
+ "logLevel": "DEBUG"
+}`
+
+function event_handler(event) {
+ console.log("evento NodeJs: " + event)
+}
+
+wakuMod.wakuNew(cfg)
+
+wakuMod.wakuVersion(function(msg){ console.log("Waku Version: " + msg) })
+
+// Example on how to retrieve a value from the waku library
+var defaultPubsubTopic = ""
+wakuMod.wakuDefaultPubsubTopic(function(msg){ defaultPubsubTopic = msg })
+
+console.log("Default pubsub topic: " + defaultPubsubTopic)
+
+console.log("Setting callback event callback function")
+wakuMod.wakuSetEventCallback(event_handler)
+
+wakuMod.wakuStart()
+
+wakuMod.wakuConnect("/ip4/127.0.0.1/tcp/60000/p2p/16Uiu2HAmVFXtAfSj4EiR7mL2KvL4EE2wztuQgUSBoj2Jx2KeXFLN",
+ 10000,
+ function onErr(msg) {
+ console.log("Error connecting node: " + msg)
+ })
+
+wakuMod.wakuRelaySubscribe(defaultPubsubTopic,
+ function onErr(msg) {
+ console.log("Error subscribing: " + msg)
+ })
+
+app.post('/publish',
+ function (req, res) {
+ // First read existing users.
+ console.log("Publish event received")
+
+ wakuMod.wakuRelayPublish(defaultPubsubTopic,
+ "content_topic_name",
+ create_random_string(10),
+ 10000,
+ function onError(msg) {
+ console.log("Error: " + msg)
+ process.exit(-1)
+ });
+
+ res.end( JSON.stringify("OK publish"))
+ })
+
+var server = app.listen(8081, function () {
+ var host = server.address().address
+ var port = server.address().port
+ console.log("Example waku listening at http://%s:%s", host, port)
+})
diff --git a/third-party/nwaku/examples/nodejs/waku_addon.c b/third-party/nwaku/examples/nodejs/waku_addon.c
new file mode 100644
index 0000000..643c90d
--- /dev/null
+++ b/third-party/nwaku/examples/nodejs/waku_addon.c
@@ -0,0 +1,586 @@
+#include
+#include
+#include
+#include
+#include
+
+#include "../cbindings/base64.h"
+#include "../../library/libwaku.h"
+
+// Reference to the NodeJs function to be called when a waku event occurs.
+// static napi_ref ref_event_callback = NULL;
+static napi_ref ref_version_callback = NULL;
+static napi_ref ref_def_pubsub_topic_callback = NULL;
+static napi_ref ref_on_error_callback = NULL;
+static napi_threadsafe_function thsafe_fn = NULL;
+
+// As a convenience, wrap N-API calls such that they cause Node.js to abort
+// when they are unsuccessful.
+#define NAPI_CALL(call) \
+do { \
+ napi_status status = call; \
+ if (status != napi_ok) { \
+ napi_fatal_error(#call, NAPI_AUTO_LENGTH, "failed", NAPI_AUTO_LENGTH); \
+ } \
+} while (0)
+
+#define WAKU_CALL(call) \
+do { \
+ int ret = call; \
+ if (ret != 0) { \
+ char msg[128]; \
+ snprintf(msg, 128, "WAKU_CALL failed with code %d", ret); \
+ napi_fatal_error(#call, NAPI_AUTO_LENGTH, "failed", NAPI_AUTO_LENGTH); \
+ } \
+} while (0)
+
+// libwaku Context
+void* ctx;
+
+// For the case of C language we don't need to store a particular userData
+void* userData = NULL;
+
+static napi_env my_env;
+
+// This function is responsible for converting data coming in from the worker
+// thread to napi_value items that can be passed into JavaScript, and for
+// calling the JavaScript function.
+static void CallJs(napi_env env, napi_value js_cb, void* context, void* data) {
+
+ // This parameter is not used.
+ (void) context;
+ // napi_status status;
+
+ // Retrieve the message item created by the worker thread.
+ char* msg = (char*) data;
+
+ // env and js_cb may both be NULL if Node.js is in its cleanup phase, and
+ // items are left over from earlier thread-safe calls from the worker thread.
+ // When env is NULL, we simply skip over the call into Javascript and free the
+ // items.
+ if (env != NULL) {
+ napi_value undefined;
+
+ // Convert the integer to a napi_value.
+ napi_value napi_msg;
+ NAPI_CALL(napi_create_string_utf8(my_env,
+ msg,
+ NAPI_AUTO_LENGTH,
+ &napi_msg));
+
+ // Retrieve the JavaScript `undefined` value so we can use it as the `this`
+ // value of the JavaScript function call.
+ NAPI_CALL(napi_get_undefined(env, &undefined));
+
+ // Call the JavaScript function and pass it the message generated in the
+ // working thread.
+ NAPI_CALL(napi_call_function(env,
+ undefined,
+ js_cb,
+ 1,
+ &napi_msg,
+ NULL));
+ }
+
+ // Free the item created by the worker thread.
+ free(data);
+}
+
+void handle_waku_version(int callerRet, const char* msg, size_t len) {
+ if (ref_version_callback == NULL) {
+ napi_throw_type_error(my_env, NULL, "ERROR in event_handler. ref_version_callback == NULL");
+ }
+
+ napi_value callback;
+ NAPI_CALL(napi_get_reference_value(my_env, ref_version_callback, &callback));
+
+ size_t argc = 2;
+ napi_value napi_msg;
+ NAPI_CALL(napi_create_string_utf8(my_env,
+ msg,
+ NAPI_AUTO_LENGTH,
+ &napi_msg));
+ napi_value napi_len;
+ NAPI_CALL(napi_create_int32(my_env,
+ len,
+ &napi_len));
+
+ napi_value global;
+ NAPI_CALL(napi_get_global(my_env, &global));
+ NAPI_CALL(napi_call_function(my_env, global, callback, argc, &napi_msg, NULL));
+}
+
+// This function is directly passed as a callback to the libwaku and it
+// calls a NodeJs function if it has been set.
+void event_handler(int callerRet, const char* msg, size_t len) {
+ if (thsafe_fn == NULL) {
+ // if (ref_event_callback == NULL) {
+ napi_throw_type_error(my_env, NULL, "ERROR in event_handler. ref_event_callback == NULL");
+ }
+
+ char* allocated_msg = malloc(len + 1);
+ strcpy(allocated_msg, msg);
+
+ NAPI_CALL(napi_call_threadsafe_function(thsafe_fn, allocated_msg, napi_tsfn_nonblocking));
+}
+
+void handle_error(int callerRet, const char* msg, size_t len) {
+ if (ref_on_error_callback == NULL) {
+ napi_throw_type_error(my_env, NULL, "ERROR in event_handler. ref_on_error_callback == NULL");
+ }
+
+ napi_value callback;
+ NAPI_CALL(napi_get_reference_value(my_env,
+ ref_on_error_callback,
+ &callback));
+ size_t argc = 2;
+ napi_value napi_msg;
+ NAPI_CALL(napi_create_string_utf8(my_env,
+ msg,
+ NAPI_AUTO_LENGTH,
+ &napi_msg));
+ napi_value global;
+ NAPI_CALL(napi_get_global(my_env, &global));
+ NAPI_CALL(napi_call_function(my_env, global, callback, argc, &napi_msg, NULL));
+}
+
+char* contentTopic = NULL;
+void handle_content_topic(int callerRet, const char* msg, size_t len) {
+ if (contentTopic != NULL) {
+ free(contentTopic);
+ }
+
+ contentTopic = malloc(len * sizeof(char) + 1);
+ strcpy(contentTopic, msg);
+}
+
+void handle_default_pubsub_topic(int callerRet, const char* msg, size_t len) {
+ if (ref_def_pubsub_topic_callback == NULL) {
+ napi_throw_type_error(my_env, NULL,
+ "ERROR in event_handler. ref_def_pubsub_topic_callback == NULL");
+ }
+
+ napi_value callback;
+ NAPI_CALL(napi_get_reference_value(my_env,
+ ref_def_pubsub_topic_callback,
+ &callback));
+ size_t argc = 2;
+ napi_value napi_msg;
+ NAPI_CALL(napi_create_string_utf8(my_env,
+ msg,
+ NAPI_AUTO_LENGTH,
+ &napi_msg));
+ napi_value napi_len;
+ NAPI_CALL(napi_create_int32(my_env,
+ len,
+ &napi_len));
+
+ napi_value global;
+ NAPI_CALL(napi_get_global(my_env, &global));
+ NAPI_CALL(napi_call_function(my_env, global, callback, argc, &napi_msg, NULL));
+}
+
+// The next should be called always, at the beginning
+static napi_value WakuNew(napi_env env, napi_callback_info info) {
+
+ size_t argc = 1;
+ napi_value args[1];
+ NAPI_CALL(napi_get_cb_info(env, info, &argc, args, NULL, NULL));
+
+ if (argc < 1) {
+ napi_throw_type_error(env, NULL, "Wrong number of arguments");
+ return NULL;
+ }
+
+ size_t str_size;
+ size_t str_size_read;
+ napi_get_value_string_utf8(env, args[0], NULL, 0, &str_size);
+ char* jsonConfig;
+ jsonConfig = malloc(str_size + 1);
+ str_size = str_size + 1;
+ napi_get_value_string_utf8(env, args[0], jsonConfig, str_size, &str_size_read);
+
+ ctx = waku_new(jsonConfig, event_handler, userData);
+
+ free(jsonConfig);
+
+ return NULL;
+}
+
+static napi_value WakuVersion(napi_env env, napi_callback_info info) {
+ size_t argc = 1;
+ napi_value args[1];
+ NAPI_CALL(napi_get_cb_info(env, info, &argc, args, NULL, NULL));
+
+ if (argc < 1) {
+ napi_throw_type_error(env, NULL, "Wrong number of arguments");
+ return NULL;
+ }
+
+ napi_value cb = args[0];
+
+ napi_valuetype valueType;
+ NAPI_CALL(napi_typeof(env, cb, &valueType));
+
+ if (valueType != napi_function) {
+ napi_throw_type_error(env, NULL, "The argument should be a napi_function");
+ return NULL;
+ }
+
+ my_env = env;
+ if(ref_version_callback != NULL) {
+ NAPI_CALL(napi_delete_reference(env, ref_version_callback));
+ }
+
+ NAPI_CALL(napi_create_reference(env, cb, 1, &ref_version_callback));
+
+ WAKU_CALL( waku_version(ctx, handle_waku_version, userData) );
+
+ return NULL;
+}
+
+static napi_value WakuSetEventCallback(napi_env env, napi_callback_info info) {
+
+ size_t argc = 1;
+ napi_value args[1];
+ NAPI_CALL(napi_get_cb_info(env, info, &argc, args, NULL, NULL));
+
+ if (argc < 1) {
+ napi_throw_type_error(env, NULL, "Wrong number of arguments");
+ return NULL;
+ }
+
+ napi_value cb = args[0];
+
+ napi_valuetype valueType;
+ NAPI_CALL(napi_typeof(env, cb, &valueType));
+
+ if (valueType != napi_function) {
+ napi_throw_type_error(env, NULL, "The argument should be a napi_function");
+ return NULL;
+ }
+
+ my_env = env;
+
+ napi_value work_name;
+ NAPI_CALL(napi_create_string_utf8(env,
+ "worker_name",
+ NAPI_AUTO_LENGTH,
+ &work_name));
+
+ NAPI_CALL(
+ napi_create_threadsafe_function(env,
+ cb,
+ NULL,
+ work_name,
+ 0,
+ 1,
+ NULL,
+ NULL,
+ NULL,
+ CallJs, // the C/C++ callback function
+ // out: the asynchronous thread-safe JavaScript function
+ &thsafe_fn));
+
+ // Inside 'event_handler', the event will be dispatched to the NodeJs
+ // if there is a proper napi_function (ref_event_callback) being set.
+ waku_set_event_callback(event_handler, userData);
+
+ return NULL;
+}
+
+static napi_value WakuStart(napi_env env, napi_callback_info info) {
+ waku_start(ctx, event_handler, userData);
+ return NULL;
+}
+
+static napi_value WakuConnect(napi_env env, napi_callback_info info) {
+ size_t argc = 3;
+ napi_value args[3];
+ NAPI_CALL(napi_get_cb_info(env, info, &argc, args, NULL, NULL));
+
+ if (argc < 3) {
+ napi_throw_type_error(env, NULL, "Wrong number of arguments");
+ return NULL;
+ }
+
+ // Getting the peers param
+ napi_value napiPeers = args[0];
+ napi_valuetype valueType;
+ NAPI_CALL(napi_typeof(env, napiPeers, &valueType));
+
+ if (valueType != napi_string) {
+ napi_throw_type_error(env, NULL, "The peers attribute should be a string");
+ return NULL;
+ }
+
+ size_t str_size;
+ size_t str_size_read;
+ napi_get_value_string_utf8(env, napiPeers, NULL, 0, &str_size);
+ char* peers;
+ peers = malloc(str_size + 1);
+ str_size = str_size + 1;
+ napi_get_value_string_utf8(env, napiPeers, peers, str_size, &str_size_read);
+
+ // Getting the timeout param
+ napi_value napiTimeout = args[1];
+ NAPI_CALL(napi_typeof(env, napiTimeout, &valueType));
+
+ if (valueType != napi_number) {
+ napi_throw_type_error(env, NULL, "The timeout attribute should be a number");
+ return NULL;
+ }
+
+ int32_t timeoutMs;
+ NAPI_CALL(napi_get_value_int32(env, napiTimeout, &timeoutMs));
+
+ // Getting the 'onError' callback
+ napi_value cb = args[2];
+
+ NAPI_CALL(napi_typeof(env, cb, &valueType));
+
+ if (valueType != napi_function) {
+ napi_throw_type_error(env, NULL, "The argument should be a napi_function");
+ return NULL;
+ }
+
+ my_env = env;
+ NAPI_CALL(napi_create_reference(env, cb, 1, &ref_on_error_callback));
+
+ WAKU_CALL(waku_connect(ctx, peers, timeoutMs, handle_error, userData));
+
+ // Free allocated memory
+ free(peers);
+
+ return NULL;
+}
+
+static napi_value WakuRelayPublish(napi_env env, napi_callback_info info) {
+ size_t argc = 5;
+ napi_value args[5];
+ NAPI_CALL(napi_get_cb_info(env, info, &argc, args, NULL, NULL));
+
+ if (argc < 5) {
+ napi_throw_type_error(env, NULL, "Wrong number of arguments");
+ return NULL;
+ }
+
+ // pubsubtopic
+ napi_value napiPubsubTopic = args[0];
+ napi_valuetype valueType;
+ NAPI_CALL(napi_typeof(env, napiPubsubTopic, &valueType));
+
+ if (valueType != napi_string) {
+ napi_throw_type_error(env, NULL, "The napiPubsubTopic attribute should be a string");
+ return NULL;
+ }
+
+ size_t str_size;
+ size_t str_size_read;
+ napi_get_value_string_utf8(env, napiPubsubTopic, NULL, 0, &str_size);
+ char* pubsub_topic;
+ pubsub_topic = malloc(str_size + 1);
+ str_size = str_size + 1;
+ napi_get_value_string_utf8(env, napiPubsubTopic, pubsub_topic, str_size, &str_size_read);
+
+ // content topic
+ napi_value napiContentTopic = args[1];
+ NAPI_CALL(napi_typeof(env, napiContentTopic, &valueType));
+
+ if (valueType != napi_string) {
+ napi_throw_type_error(env, NULL, "The content topic attribute should be a string");
+ return NULL;
+ }
+
+ napi_get_value_string_utf8(env, napiContentTopic, NULL, 0, &str_size);
+ char* content_topic_name = malloc(str_size + 1);
+ str_size = str_size + 1;
+ napi_get_value_string_utf8(env, napiContentTopic, content_topic_name, str_size, &str_size_read);
+
+ // message
+ napi_value napiMessage = args[2];
+ NAPI_CALL(napi_typeof(env, napiMessage, &valueType));
+
+ if (valueType != napi_string) {
+ napi_throw_type_error(env, NULL, "The message attribute should be a string");
+ return NULL;
+ }
+
+ char msg[2048];
+ // TODO: check the correct message size limit
+ size_t lengthMsg;
+ NAPI_CALL(napi_get_value_string_utf8(env,
+ napiMessage,
+ msg,
+ 2048,
+ &lengthMsg));
+ char jsonWakuMsg[1024];
+ char *msgPayload = b64_encode((unsigned char*) msg, strlen(msg));
+
+ // TODO: move all the 'waku_content_topic' logic inside the libwaku
+ WAKU_CALL( waku_content_topic(ctx,
+ "appName",
+ 1,
+ content_topic_name,
+ "encoding",
+ handle_content_topic,
+ userData) );
+ snprintf(jsonWakuMsg,
+ 1024,
+ "{\"payload\":\"%s\",\"content_topic\":\"%s\"}",
+ msgPayload, contentTopic);
+ free(msgPayload);
+
+ // Getting the timeout parameter
+ unsigned int timeoutMs;
+ napi_value timeout = args[3];
+
+ NAPI_CALL(napi_typeof(env, timeout, &valueType));
+
+ if (valueType != napi_number) {
+ napi_throw_type_error(env, NULL, "The argument should be a napi_number");
+ return NULL;
+ }
+
+ NAPI_CALL(napi_get_value_int64(env, timeout, (int64_t *) &timeoutMs));
+
+ // Getting the 'onError' callback
+ napi_value cb = args[4];
+
+ NAPI_CALL(napi_typeof(env, cb, &valueType));
+
+ if (valueType != napi_function) {
+ napi_throw_type_error(env, NULL, "The argument should be a napi_function");
+ return NULL;
+ }
+
+ NAPI_CALL(napi_create_reference(env, cb, 1, &ref_on_error_callback));
+
+ // Perform the actual 'publish'
+ WAKU_CALL( waku_relay_publish(ctx,
+ pubsub_topic,
+ jsonWakuMsg,
+ timeoutMs,
+ handle_error,
+ userData) );
+ free(pubsub_topic);
+ free(content_topic_name);
+
+ return NULL;
+}
+
+static napi_value WakuDefaultPubsubTopic(napi_env env, napi_callback_info info) {
+ size_t argc = 1;
+ napi_value args[1];
+ NAPI_CALL(napi_get_cb_info(env, info, &argc, args, NULL, NULL));
+
+ if (argc < 1) {
+ napi_throw_type_error(env, NULL, "Wrong number of arguments");
+ return NULL;
+ }
+
+ napi_value cb = args[0];
+
+ napi_valuetype valueType;
+ NAPI_CALL(napi_typeof(env, cb, &valueType));
+
+ if (valueType != napi_function) {
+ napi_throw_type_error(env, NULL, "The argument should be a napi_function");
+ return NULL;
+ }
+
+ my_env = env;
+ if(ref_def_pubsub_topic_callback != NULL) {
+ NAPI_CALL(napi_delete_reference(env, ref_def_pubsub_topic_callback));
+ }
+
+ NAPI_CALL(napi_create_reference(env, cb, 1, &ref_def_pubsub_topic_callback));
+
+ WAKU_CALL( waku_default_pubsub_topic(ctx, handle_default_pubsub_topic, userData) );
+
+ return NULL;
+}
+
+static napi_value WakuRelaySubscribe(napi_env env, napi_callback_info info) {
+ size_t argc = 2;
+ napi_value args[2];
+ NAPI_CALL(napi_get_cb_info(env, info, &argc, args, NULL, NULL));
+
+ if (argc < 2) {
+ napi_throw_type_error(env, NULL, "Wrong number of arguments");
+ return NULL;
+ }
+
+ // Getting the pubsub topic param
+ napi_value topic = args[0];
+ napi_valuetype valueType;
+ NAPI_CALL(napi_typeof(env, topic, &valueType));
+
+ if (valueType != napi_string) {
+ napi_throw_type_error(env, NULL, "The topic attribute should be a string");
+ return NULL;
+ }
+
+ size_t str_size;
+ size_t str_size_read;
+ napi_get_value_string_utf8(env, topic, NULL, 0, &str_size);
+ char* pubsub_topic;
+ pubsub_topic = malloc(str_size + 1);
+ str_size = str_size + 1;
+ napi_get_value_string_utf8(env, topic, pubsub_topic, str_size, &str_size_read);
+
+ // Getting the 'onError' callback
+ napi_value cb = args[1];
+
+ NAPI_CALL(napi_typeof(env, cb, &valueType));
+
+ if (valueType != napi_function) {
+ napi_throw_type_error(env, NULL, "The argument should be a napi_function");
+ return NULL;
+ }
+
+ my_env = env;
+ NAPI_CALL(napi_create_reference(env, cb, 1, &ref_on_error_callback));
+
+ // Calling the actual 'subscribe' waku function
+ WAKU_CALL( waku_relay_subscribe(ctx, pubsub_topic, handle_error, userData) );
+
+ free(pubsub_topic);
+
+ return NULL;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, 0, func, 0, 0, 0, napi_default, 0 }
+
+static napi_value Init(napi_env env, napi_value exports) {
+ // DECLARE_NAPI_METHOD("", );
+
+ napi_property_descriptor wakuVersion = DECLARE_NAPI_METHOD("wakuVersion", WakuVersion);
+ NAPI_CALL(napi_define_properties(env, exports, 1, &wakuVersion));
+
+ napi_property_descriptor wakuNew = DECLARE_NAPI_METHOD("wakuNew", WakuNew);
+ NAPI_CALL(napi_define_properties(env, exports, 1, &wakuNew));
+
+ napi_property_descriptor wakuStart = DECLARE_NAPI_METHOD("wakuStart", WakuStart);
+ NAPI_CALL(napi_define_properties(env, exports, 1, &wakuStart));
+
+ napi_property_descriptor wakuSetEventCallback = DECLARE_NAPI_METHOD("wakuSetEventCallback", WakuSetEventCallback);
+ NAPI_CALL(napi_define_properties(env, exports, 1, &wakuSetEventCallback));
+
+ napi_property_descriptor wakuDefaultPubsubTopic = DECLARE_NAPI_METHOD("wakuDefaultPubsubTopic", WakuDefaultPubsubTopic);
+ NAPI_CALL(napi_define_properties(env, exports, 1, &wakuDefaultPubsubTopic));
+
+ napi_property_descriptor wakuRelaySubscribe = DECLARE_NAPI_METHOD("wakuRelaySubscribe", WakuRelaySubscribe);
+ NAPI_CALL(napi_define_properties(env, exports, 1, &wakuRelaySubscribe));
+
+ napi_property_descriptor wakuConnect = DECLARE_NAPI_METHOD("wakuConnect", WakuConnect);
+ NAPI_CALL(napi_define_properties(env, exports, 1, &wakuConnect));
+
+ napi_property_descriptor wakuRelayPublish = DECLARE_NAPI_METHOD("wakuRelayPublish", WakuRelayPublish);
+ NAPI_CALL(napi_define_properties(env, exports, 1, &wakuRelayPublish));
+
+ return exports;
+}
+
+NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
diff --git a/third-party/nwaku/examples/publisher.nim b/third-party/nwaku/examples/publisher.nim
new file mode 100644
index 0000000..8c2d036
--- /dev/null
+++ b/third-party/nwaku/examples/publisher.nim
@@ -0,0 +1,142 @@
+import
+ std/[tables, times, sequtils],
+ stew/byteutils,
+ chronicles,
+ chronos,
+ confutils,
+ libp2p/crypto/crypto,
+ eth/keys,
+ eth/p2p/discoveryv5/enr
+
+import
+ waku/[
+ common/logging,
+ node/peer_manager,
+ waku_core,
+ waku_node,
+ waku_enr,
+ discovery/waku_discv5,
+ factory/builder,
+ ]
+
+proc now*(): Timestamp =
+ getNanosecondTime(getTime().toUnixFloat())
+
+# An accesible bootstrap node. See waku.sandbox fleets.status.im
+const bootstrapNode =
+ "enr:-QEkuEB3WHNS-xA3RDpfu9A2Qycr3bN3u7VoArMEiDIFZJ6" &
+ "6F1EB3d4wxZN1hcdcOX-RfuXB-MQauhJGQbpz3qUofOtLAYJpZI" &
+ "J2NIJpcIQI2SVcim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmFjL" &
+ "WNuLWhvbmdrb25nLWMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2" &
+ "XwA2Ni9ub2RlLTAxLmFjLWNuLWhvbmdrb25nLWMud2FrdS5zYW5" &
+ "kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQ" &
+ "AGAAeJc2VjcDI1NmsxoQPK35Nnz0cWUtSAhBp7zvHEhyU_AqeQU" &
+ "lqzLiLxfP2L4oN0Y3CCdl-DdWRwgiMohXdha3UyDw"
+
+# careful if running pub and sub in the same machine
+const wakuPort = 60000
+const discv5Port = 9000
+
+proc setupAndPublish(rng: ref HmacDrbgContext) {.async.} =
+ # use notice to filter all waku messaging
+ setupLog(logging.LogLevel.NOTICE, logging.LogFormat.TEXT)
+
+ notice "starting publisher", wakuPort = wakuPort, discv5Port = discv5Port
+ let
+ nodeKey = crypto.PrivateKey.random(Secp256k1, rng[]).get()
+ ip = parseIpAddress("0.0.0.0")
+ flags = CapabilitiesBitfield.init(relay = true)
+
+ var enrBuilder = EnrBuilder.init(nodeKey)
+
+ let recordRes = enrBuilder.build()
+ let record =
+ if recordRes.isErr():
+ error "failed to create enr record", error = recordRes.error
+ quit(QuitFailure)
+ else:
+ recordRes.get()
+
+ var builder = WakuNodeBuilder.init()
+ builder.withNodeKey(nodeKey)
+ builder.withRecord(record)
+ builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet()
+ let node = builder.build().tryGet()
+
+ var bootstrapNodeEnr: enr.Record
+ discard bootstrapNodeEnr.fromURI(bootstrapNode)
+
+ let discv5Conf = WakuDiscoveryV5Config(
+ discv5Config: none(DiscoveryConfig),
+ address: ip,
+ port: Port(discv5Port),
+ privateKey: keys.PrivateKey(nodeKey.skkey),
+ bootstrapRecords: @[bootstrapNodeEnr],
+ autoupdateRecord: true,
+ )
+
+ # assumes behind a firewall, so not care about being discoverable
+ let wakuDiscv5 = WakuDiscoveryV5.new(
+ node.rng,
+ discv5Conf,
+ some(node.enr),
+ some(node.peerManager),
+ node.topicSubscriptionQueue,
+ )
+
+ await node.start()
+ (await node.mountRelay()).isOkOr:
+ error "failed to mount relay", error = error
+ quit(1)
+
+ node.peerManager.start()
+
+ (await wakuDiscv5.start()).isOkOr:
+ error "failed to start discv5", error = error
+ quit(1)
+
+ # wait for a minimum of peers to be connected, otherwise messages wont be gossiped
+ while true:
+ let numConnectedPeers = node.peerManager.switch.peerStore[ConnectionBook].book
+ .values()
+ .countIt(it == Connected)
+ if numConnectedPeers >= 6:
+ notice "publisher is ready", connectedPeers = numConnectedPeers, required = 6
+ break
+ notice "waiting to be ready", connectedPeers = numConnectedPeers, required = 6
+ await sleepAsync(5000)
+
+ # Make sure it matches the publisher. Use default value
+ # see spec: https://rfc.vac.dev/spec/23/
+ let pubSubTopic = PubsubTopic("/waku/2/rs/0/0")
+
+ # any content topic can be chosen
+ let contentTopic = ContentTopic("/examples/1/pubsub-example/proto")
+
+ notice "publisher service started"
+ while true:
+ let text = "hi there i'm a publisher"
+ let message = WakuMessage(
+ payload: toBytes(text), # content of the message
+ contentTopic: contentTopic, # content topic to publish to
+ ephemeral: true, # tell store nodes to not store it
+ timestamp: now(),
+ ) # current timestamp
+
+ let res = await node.publish(some(pubSubTopic), message)
+
+ if res.isOk:
+ notice "published message",
+ text = text,
+ timestamp = message.timestamp,
+ psTopic = pubSubTopic,
+ contentTopic = contentTopic
+ else:
+ error "failed to publish message", error = res.error
+
+ await sleepAsync(5000)
+
+when isMainModule:
+ let rng = crypto.newRng()
+ asyncSpawn setupAndPublish(rng)
+ runForever()
diff --git a/third-party/nwaku/examples/python/requirements.txt b/third-party/nwaku/examples/python/requirements.txt
new file mode 100644
index 0000000..b2c3a0d
--- /dev/null
+++ b/third-party/nwaku/examples/python/requirements.txt
@@ -0,0 +1,7 @@
+blinker==1.6.2
+click==8.1.6
+Flask==2.3.2
+itsdangerous==2.1.2
+Jinja2==3.1.2
+MarkupSafe==2.1.3
+Werkzeug==2.3.6
diff --git a/third-party/nwaku/examples/python/waku.py b/third-party/nwaku/examples/python/waku.py
new file mode 100644
index 0000000..4d5f564
--- /dev/null
+++ b/third-party/nwaku/examples/python/waku.py
@@ -0,0 +1,154 @@
+from flask import Flask
+import ctypes
+import argparse
+
+libwaku = object
+try:
+ # This python script should be run from the root repo folder
+ libwaku = ctypes.CDLL("build/libwaku.so")
+except Exception as e:
+ print("Exception: ", e)
+ print("""
+The 'libwaku.so' library can be created with the next command from
+the repo's root folder: `make libwaku`.
+
+And it should build the library in 'build/libwaku.so'.
+
+Therefore, make sure the LD_LIBRARY_PATH env var points at the location that
+contains the 'libwaku.so' library.
+""")
+ exit(-1)
+
+def handle_event(ret, msg, user_data):
+ print("Event received: %s" % msg)
+
+def call_waku(func):
+ ret = func()
+ if (ret != 0):
+ print("Error in %s. Error code: %d" % (locals().keys(), ret))
+ exit(1)
+
+# Parse params
+parser = argparse.ArgumentParser(description='libwaku integration in Python.')
+parser.add_argument('-d', '--host', dest='host', default='0.0.0.0',
+ help='Address this node will listen to. [=0.0.0.0]')
+parser.add_argument('-p', '--port', dest='port', default=60000, required=True,
+ help='Port this node will listen to. [=60000]')
+parser.add_argument('-k', '--key', dest='key', default="", required=True,
+ help="""P2P node private key as 64 char hex string.
+e.g.: 364d111d729a6eb6d2e6113e163f017b5ef03a6f94c9b5b7bb1bb36fa5cb07a9""")
+parser.add_argument('-r', '--relay', dest='relay', default="true",
+ help="Enable relay protocol: true|false [=true]")
+parser.add_argument('--peer', dest='peer', default="",
+ help="Multiqualified libp2p address")
+
+args = parser.parse_args()
+
+# The next 'json_config' is the item passed to the 'waku_new'.
+json_config = "{ \
+ \"host\": \"%s\", \
+ \"port\": %d, \
+ \"key\": \"%s\", \
+ \"relay\": %s ,\
+ \"logLevel\": \"DEBUG\" \
+ }" % (args.host,
+ int(args.port),
+ args.key,
+ "true" if args.relay else "false")
+
+callback_type = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p, ctypes.c_size_t)
+
+# Node creation
+libwaku.waku_new.restype = ctypes.c_void_p
+libwaku.waku_new.argtypes = [ctypes.c_char_p,
+ callback_type,
+ ctypes.c_void_p]
+
+ctx = libwaku.waku_new(bytes(json_config, 'utf-8'),
+ callback_type(
+ #onErrCb
+ lambda ret, msg, len:
+ print("Error calling waku_new: %s",
+ msg.decode('utf-8'))
+ ),
+ ctypes.c_void_p(0))
+
+# Retrieve the current version of the library
+libwaku.waku_version.argtypes = [ctypes.c_void_p,
+ callback_type,
+ ctypes.c_void_p]
+libwaku.waku_version(ctx,
+ callback_type(lambda ret, msg, len:
+ print("Git Version: %s" %
+ msg.decode('utf-8'))),
+ ctypes.c_void_p(0))
+
+# Retrieve the default pubsub topic
+default_pubsub_topic = ""
+libwaku.waku_default_pubsub_topic.argtypes = [ctypes.c_void_p,
+ callback_type,
+ ctypes.c_void_p]
+libwaku.waku_default_pubsub_topic(ctx,
+ callback_type(
+ lambda ret, msg, len: (
+ globals().update(default_pubsub_topic = msg.decode('utf-8')),
+ print("Default pubsub topic: %s" % msg.decode('utf-8')))
+ ),
+ ctypes.c_void_p(0))
+
+print("Bind addr: {}:{}".format(args.host, args.port))
+print("Waku Relay enabled: {}".format(args.relay))
+
+# Set the event callback
+callback = callback_type(handle_event) # This line is important so that the callback is not gc'ed
+
+libwaku.waku_set_event_callback.argtypes = [callback_type, ctypes.c_void_p]
+libwaku.waku_set_event_callback(callback, ctypes.c_void_p(0))
+
+# Start the node
+libwaku.waku_start.argtypes = [ctypes.c_void_p,
+ callback_type,
+ ctypes.c_void_p]
+libwaku.waku_start(ctx,
+ callback_type(lambda ret, msg, len:
+ print("Error in waku_start: %s" %
+ msg.decode('utf-8'))),
+ ctypes.c_void_p(0))
+
+# Subscribe to the default pubsub topic
+libwaku.waku_relay_subscribe.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p,
+ callback_type,
+ ctypes.c_void_p]
+libwaku.waku_relay_subscribe(ctx,
+ default_pubsub_topic.encode('utf-8'),
+ callback_type(
+ #onErrCb
+ lambda ret, msg, len:
+ print("Error calling waku_relay_subscribe: %s" %
+ msg.decode('utf-8'))
+ ),
+ ctypes.c_void_p(0))
+
+libwaku.waku_connect.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_int,
+ callback_type,
+ ctypes.c_void_p]
+libwaku.waku_connect(ctx,
+ args.peer.encode('utf-8'),
+ 10000,
+ # onErrCb
+ callback_type(
+ lambda ret, msg, len:
+ print("Error calling waku_connect: %s" % msg.decode('utf-8'))),
+ ctypes.c_void_p(0))
+
+# app = Flask(__name__)
+# @app.route("/")
+# def hello_world():
+# return "Hello, World!"
+
+# Simply avoid the app to
+a = input()
+
diff --git a/third-party/nwaku/examples/qt/Makefile b/third-party/nwaku/examples/qt/Makefile
new file mode 100644
index 0000000..aa2147e
--- /dev/null
+++ b/third-party/nwaku/examples/qt/Makefile
@@ -0,0 +1,26 @@
+
+## Has been compiled with Qt 5.15.2
+
+## If change the main.qml, the qmake should be called
+## This may be needed in Ubuntu: sudo apt install qtdeclarative5-dev qtquickcontrols2-5-dev
+
+CXX = g++
+CXXFLAGS = -g3 -fpermissive -fPIC `pkg-config --cflags Qt5Core Qt5Gui Qt5Qml Qt5Quick`
+LDFLAGS = `pkg-config --libs Qt5Core Qt5Gui Qt5Qml Qt5Quick` -lwaku -L../../build/
+MOC = moc
+
+TARGET = main-qt
+SRC = main_qt.cpp
+MOC_SRC = waku_handler.moc.cpp
+HEADERS = waku_handler.h
+
+all: $(TARGET)
+
+$(MOC_SRC): $(HEADERS)
+ $(MOC) $< -o $@
+
+$(TARGET): $(SRC) $(MOC_SRC)
+ $(CXX) $(CXXFLAGS) -o $(TARGET) $(SRC) $(MOC_SRC) $(LDFLAGS)
+
+clean:
+ rm -f $(TARGET) $(MOC_SRC)
diff --git a/third-party/nwaku/examples/qt/main.qml b/third-party/nwaku/examples/qt/main.qml
new file mode 100644
index 0000000..7ef2dcc
--- /dev/null
+++ b/third-party/nwaku/examples/qt/main.qml
@@ -0,0 +1,64 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+
+ApplicationWindow {
+ visible: true
+ width: 400
+ height: 300
+ title: "Hello, World!"
+
+ Column {
+ anchors.centerIn: parent
+ spacing: 20
+
+ Label {
+ text: "Hello, World!"
+ font.pixelSize: 24
+ horizontalAlignment: Text.AlignHCenter
+ }
+ }
+
+ Rectangle {
+ width: parent.width
+ height: 60
+ anchors.bottom: parent.bottom
+ color: "transparent"
+
+ Row {
+ anchors.centerIn: parent
+ spacing: 30
+
+ Button {
+ text: "Start Waku Node"
+ width: 150
+ height: 40
+ font.pixelSize: 16
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: wakuHandler.start()
+ }
+ background: Rectangle {
+ color: "#2196F3"
+ radius: 10
+ }
+ }
+
+ Button {
+ text: "Stop Waku Node"
+ width: 150
+ height: 40
+ font.pixelSize: 16
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: wakuHandler.stop()
+ }
+ background: Rectangle {
+ color: "#F44336"
+ radius: 10
+ }
+ }
+ }
+ }
+}
diff --git a/third-party/nwaku/examples/qt/main_qt.cpp b/third-party/nwaku/examples/qt/main_qt.cpp
new file mode 100644
index 0000000..f16660c
--- /dev/null
+++ b/third-party/nwaku/examples/qt/main_qt.cpp
@@ -0,0 +1,46 @@
+#include
+#include
+#include
+
+#include "waku_handler.h"
+
+void event_handler(int callerRet, const char* msg, size_t len, void* userData) {
+ printf("Receiving message %s\n", msg);
+}
+
+int main(int argc, char *argv[]) {
+ QGuiApplication app(argc, argv);
+ QQmlApplicationEngine engine;
+
+ WakuHandler wakuHandler;
+ void* userData = nullptr;
+
+ QString jsonConfig = R"(
+ {
+ "tcpPort": 60000,
+ "relay": true,
+ "logLevel": "TRACE",
+ "discv5Discovery": true,
+ "discv5BootstrapNodes": [
+ "enr:-QESuEB4Dchgjn7gfAvwB00CxTA-nGiyk-aALI-H4dYSZD3rUk7bZHmP8d2U6xDiQ2vZffpo45Jp7zKNdnwDUx6g4o6XAYJpZIJ2NIJpcIRA4VDAim11bHRpYWRkcnO4XAArNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwAtNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQOvD3S3jUNICsrOILlmhENiWAMmMVlAl6-Q8wRB7hidY4N0Y3CCdl-DdWRwgiMohXdha3UyDw",
+ "enr:-QEkuEBIkb8q8_mrorHndoXH9t5N6ZfD-jehQCrYeoJDPHqT0l0wyaONa2-piRQsi3oVKAzDShDVeoQhy0uwN1xbZfPZAYJpZIJ2NIJpcIQiQlleim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwA2Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQKnGt-GSgqPSf3IAPM7bFgTlpczpMZZLF3geeoNNsxzSoN0Y3CCdl-DdWRwgiMohXdha3UyDw"
+ ],
+ "discv5UdpPort": 9999,
+ "dnsDiscovery": true,
+ "dnsDiscoveryUrl": "enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im",
+ "dnsDiscoveryNameServers": ["8.8.8.8", "1.0.0.1"]
+ }
+ )";
+
+ wakuHandler.initialize(jsonConfig, event_handler, userData);
+
+ engine.rootContext()->setContextProperty("wakuHandler", &wakuHandler);
+
+ engine.load(QUrl::fromLocalFile("main.qml"));
+
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ return app.exec();
+}
+
diff --git a/third-party/nwaku/examples/qt/qt.pro b/third-party/nwaku/examples/qt/qt.pro
new file mode 100644
index 0000000..7e1770d
--- /dev/null
+++ b/third-party/nwaku/examples/qt/qt.pro
@@ -0,0 +1,18 @@
+######################################################################
+# Automatically generated by qmake (3.1) Thu Feb 27 21:42:11 2025
+######################################################################
+
+TEMPLATE = app
+TARGET = qt
+INCLUDEPATH += .
+
+# You can make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# Please consult the documentation of the deprecated API in order to know
+# how to port your code away from it.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+# Input
+HEADERS += waku_handler.h
+SOURCES += main_qt.cpp waku_hand.moc.cpp waku_handler.moc.cpp
diff --git a/third-party/nwaku/examples/qt/waku_handler.h b/third-party/nwaku/examples/qt/waku_handler.h
new file mode 100644
index 0000000..161a17c
--- /dev/null
+++ b/third-party/nwaku/examples/qt/waku_handler.h
@@ -0,0 +1,56 @@
+#include
+#include
+#include
+
+#include "../../library/libwaku.h"
+
+class WakuHandler : public QObject {
+ Q_OBJECT
+private:
+ static void event_handler(int callerRet, const char* msg, size_t len, void* userData) {
+ printf("Receiving message %s\n", msg);
+ }
+
+ static void on_event_received(int callerRet, const char* msg, size_t len, void* userData) {
+ if (callerRet == RET_ERR) {
+ printf("Error: %s\n", msg);
+ exit(1);
+ }
+ else if (callerRet == RET_OK) {
+ printf("Receiving event: %s\n", msg);
+ }
+ }
+
+public:
+ WakuHandler() : QObject(), ctx(nullptr) {}
+
+ void initialize(const QString& jsonConfig, WakuCallBack event_handler, void* userData) {
+ ctx = waku_new(jsonConfig.toUtf8().constData(), WakuCallBack(event_handler), userData);
+
+ waku_set_event_callback(ctx, on_event_received, userData);
+ qDebug() << "Waku context initialized, ready to start.";
+ }
+
+ Q_INVOKABLE void start() {
+ if (ctx) {
+ waku_start(ctx, event_handler, nullptr);
+ qDebug() << "Waku start called with event_handler and userData.";
+ } else {
+ qDebug() << "Context is not initialized in start.";
+ }
+ }
+
+ Q_INVOKABLE void stop() {
+ if (ctx) {
+ waku_stop(ctx, event_handler, nullptr);
+ qDebug() << "Waku stop called with event_handler and userData.";
+ } else {
+ qDebug() << "Context is not initialized in stop.";
+ }
+ }
+
+ virtual ~WakuHandler() {}
+
+private:
+ void* ctx;
+};
diff --git a/third-party/nwaku/examples/rust/Cargo.lock b/third-party/nwaku/examples/rust/Cargo.lock
new file mode 100644
index 0000000..a4fea40
--- /dev/null
+++ b/third-party/nwaku/examples/rust/Cargo.lock
@@ -0,0 +1,25 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.148"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
+
+[[package]]
+name = "waku-rust-simple-example"
+version = "0.1.0"
+dependencies = [
+ "cc",
+]
diff --git a/third-party/nwaku/examples/rust/Cargo.toml b/third-party/nwaku/examples/rust/Cargo.toml
new file mode 100644
index 0000000..c9ad9fa
--- /dev/null
+++ b/third-party/nwaku/examples/rust/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "waku-rust-simple-example"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[build-dependencies]
+cc = "1.0.52"
diff --git a/third-party/nwaku/examples/rust/README.md b/third-party/nwaku/examples/rust/README.md
new file mode 100644
index 0000000..dc7bce3
--- /dev/null
+++ b/third-party/nwaku/examples/rust/README.md
@@ -0,0 +1,6 @@
+
+This represents a very simple example on how to integrate the `libwaku` library in Rust, and then, only a few `libwaku` functions are being wrapped.
+
+In [waku-rust-bindings](https://github.com/waku-org/waku-rust-bindings) you will find a complete Waku integration in Rust.
+
+
diff --git a/third-party/nwaku/examples/rust/build.rs b/third-party/nwaku/examples/rust/build.rs
new file mode 100644
index 0000000..b0398c6
--- /dev/null
+++ b/third-party/nwaku/examples/rust/build.rs
@@ -0,0 +1,5 @@
+
+fn main() {
+ println!("cargo:rustc-link-arg=-lwaku");
+ println!("cargo:rustc-link-arg=-L../../build/");
+}
diff --git a/third-party/nwaku/examples/rust/src/main.rs b/third-party/nwaku/examples/rust/src/main.rs
new file mode 100644
index 0000000..926d0e3
--- /dev/null
+++ b/third-party/nwaku/examples/rust/src/main.rs
@@ -0,0 +1,111 @@
+use std::cell::OnceCell;
+use std::ffi::CString;
+use std::os::raw::{c_char, c_int, c_void};
+use std::{slice, thread, time};
+
+pub type WakuCallback = unsafe extern "C" fn(c_int, *const c_char, usize, *const c_void);
+
+extern "C" {
+ pub fn waku_new(
+ config_json: *const u8,
+ cb: WakuCallback,
+ user_data: *const c_void,
+ ) -> *mut c_void;
+
+ pub fn waku_version(ctx: *const c_void, cb: WakuCallback, user_data: *const c_void) -> c_int;
+
+ pub fn waku_start(ctx: *const c_void, cb: WakuCallback, user_data: *const c_void) -> c_int;
+
+ pub fn waku_default_pubsub_topic(
+ ctx: *mut c_void,
+ cb: WakuCallback,
+ user_data: *const c_void,
+ ) -> *mut c_void;
+}
+
+pub unsafe extern "C" fn trampoline(
+ return_val: c_int,
+ buffer: *const c_char,
+ buffer_len: usize,
+ data: *const c_void,
+) where
+ C: FnMut(i32, &str),
+{
+ let closure = &mut *(data as *mut C);
+
+ let buffer_utf8 =
+ String::from_utf8(slice::from_raw_parts(buffer as *mut u8, buffer_len).to_vec())
+ .expect("valid utf8");
+
+ closure(return_val, &buffer_utf8);
+}
+
+pub fn get_trampoline